1*368c31abSDavid du Colombier /* 2*368c31abSDavid du Colombier * Disk cache. 3*368c31abSDavid du Colombier * 4*368c31abSDavid du Colombier * Caches raw disk blocks. Getdblock() gets a block, putdblock puts it back. 5*368c31abSDavid du Colombier * Getdblock has a mode parameter that determines i/o and access to a block: 6*368c31abSDavid du Colombier * if mode is OREAD or ORDWR, it is read from disk if not already in memory. 7*368c31abSDavid du Colombier * If mode is ORDWR or OWRITE, it is locked for exclusive use before being returned. 8*368c31abSDavid du Colombier * It is *not* marked dirty -- once changes have been made, they should be noted 9*368c31abSDavid du Colombier * by using dirtydblock() before putdblock(). 10*368c31abSDavid du Colombier * 11*368c31abSDavid du Colombier * There is a global cache lock as well as a lock on each block. 12*368c31abSDavid du Colombier * Within a thread, the cache lock can be acquired while holding a block lock, 13*368c31abSDavid du Colombier * but not vice versa; and a block cannot be locked if you already hold the lock 14*368c31abSDavid du Colombier * on another block. 15*368c31abSDavid du Colombier * 16*368c31abSDavid du Colombier * The flush proc writes out dirty blocks in batches, one batch per dirty tag. 17*368c31abSDavid du Colombier * For example, the DirtyArena blocks are all written to disk before any of the 18*368c31abSDavid du Colombier * DirtyArenaCib blocks. 19*368c31abSDavid du Colombier * 20*368c31abSDavid du Colombier * This code used to be in charge of flushing the dirty index blocks out to 21*368c31abSDavid du Colombier * disk, but updating the index turned out to benefit from extra care. 22*368c31abSDavid du Colombier * Now cached index blocks are never marked dirty. The index.c code takes 23*368c31abSDavid du Colombier * care of updating them behind our back, and uses _getdblock to update any 24*368c31abSDavid du Colombier * cached copies of the blocks as it changes them on disk. 25*368c31abSDavid du Colombier */ 26*368c31abSDavid du Colombier 27*368c31abSDavid du Colombier #include "stdinc.h" 28*368c31abSDavid du Colombier #include "dat.h" 29*368c31abSDavid du Colombier #include "fns.h" 30*368c31abSDavid du Colombier 31*368c31abSDavid du Colombier typedef struct DCache DCache; 32*368c31abSDavid du Colombier 33*368c31abSDavid du Colombier enum 34*368c31abSDavid du Colombier { 35*368c31abSDavid du Colombier HashLog = 9, 36*368c31abSDavid du Colombier HashSize = 1<<HashLog, 37*368c31abSDavid du Colombier HashMask = HashSize - 1, 38*368c31abSDavid du Colombier }; 39*368c31abSDavid du Colombier 40*368c31abSDavid du Colombier struct DCache 41*368c31abSDavid du Colombier { 42*368c31abSDavid du Colombier QLock lock; 43*368c31abSDavid du Colombier RWLock dirtylock; /* must be held to inspect or set b->dirty */ 44*368c31abSDavid du Colombier Rendez full; 45*368c31abSDavid du Colombier Round round; 46*368c31abSDavid du Colombier DBlock *free; /* list of available lumps */ 47*368c31abSDavid du Colombier u32int now; /* ticks for usage timestamps */ 48*368c31abSDavid du Colombier int size; /* max. size of any block; allocated to each block */ 49*368c31abSDavid du Colombier DBlock **heads; /* hash table for finding address */ 50*368c31abSDavid du Colombier int nheap; /* number of available victims */ 51*368c31abSDavid du Colombier DBlock **heap; /* heap for locating victims */ 52*368c31abSDavid du Colombier int nblocks; /* number of blocks allocated */ 53*368c31abSDavid du Colombier DBlock *blocks; /* array of block descriptors */ 54*368c31abSDavid du Colombier DBlock **write; /* array of block pointers to be written */ 55*368c31abSDavid du Colombier u8int *mem; /* memory for all block descriptors */ 56*368c31abSDavid du Colombier int ndirty; /* number of dirty blocks */ 57*368c31abSDavid du Colombier int maxdirty; /* max. number of dirty blocks */ 58*368c31abSDavid du Colombier Channel *ra; 59*368c31abSDavid du Colombier u8int *rabuf; 60*368c31abSDavid du Colombier u32int ramax; 61*368c31abSDavid du Colombier u32int rasize; 62*368c31abSDavid du Colombier u64int raaddr; 63*368c31abSDavid du Colombier Part *rapart; 64*368c31abSDavid du Colombier 65*368c31abSDavid du Colombier AState diskstate; 66*368c31abSDavid du Colombier AState state; 67*368c31abSDavid du Colombier }; 68*368c31abSDavid du Colombier 69*368c31abSDavid du Colombier typedef struct Ra Ra; 70*368c31abSDavid du Colombier struct Ra 71*368c31abSDavid du Colombier { 72*368c31abSDavid du Colombier Part *part; 73*368c31abSDavid du Colombier u64int addr; 74*368c31abSDavid du Colombier }; 75*368c31abSDavid du Colombier 76*368c31abSDavid du Colombier static DCache dcache; 77*368c31abSDavid du Colombier 78*368c31abSDavid du Colombier static int downheap(int i, DBlock *b); 79*368c31abSDavid du Colombier static int upheap(int i, DBlock *b); 80*368c31abSDavid du Colombier static DBlock *bumpdblock(void); 81*368c31abSDavid du Colombier static void delheap(DBlock *db); 82*368c31abSDavid du Colombier static void fixheap(int i, DBlock *b); 83*368c31abSDavid du Colombier static void flushproc(void*); 84*368c31abSDavid du Colombier static void writeproc(void*); 85*368c31abSDavid du Colombier static void raproc(void*); 86*368c31abSDavid du Colombier 87*368c31abSDavid du Colombier void 88*368c31abSDavid du Colombier initdcache(u32int mem) 89*368c31abSDavid du Colombier { 90*368c31abSDavid du Colombier DBlock *b, *last; 91*368c31abSDavid du Colombier u32int nblocks, blocksize; 92*368c31abSDavid du Colombier int i; 93*368c31abSDavid du Colombier u8int *p; 94*368c31abSDavid du Colombier 95*368c31abSDavid du Colombier if(mem < maxblocksize * 2) 96*368c31abSDavid du Colombier sysfatal("need at least %d bytes for the disk cache", maxblocksize * 2); 97*368c31abSDavid du Colombier if(maxblocksize == 0) 98*368c31abSDavid du Colombier sysfatal("no max. block size given for disk cache"); 99*368c31abSDavid du Colombier blocksize = maxblocksize; 100*368c31abSDavid du Colombier nblocks = mem / blocksize; 101*368c31abSDavid du Colombier dcache.full.l = &dcache.lock; 102*368c31abSDavid du Colombier dcache.nblocks = nblocks; 103*368c31abSDavid du Colombier dcache.maxdirty = (nblocks * 2) / 3; 104*368c31abSDavid du Colombier trace(TraceProc, "initialize disk cache with %d blocks of %d bytes, maximum %d dirty blocks\n", 105*368c31abSDavid du Colombier nblocks, blocksize, dcache.maxdirty); 106*368c31abSDavid du Colombier dcache.size = blocksize; 107*368c31abSDavid du Colombier dcache.heads = MKNZ(DBlock*, HashSize); 108*368c31abSDavid du Colombier dcache.heap = MKNZ(DBlock*, nblocks); 109*368c31abSDavid du Colombier dcache.blocks = MKNZ(DBlock, nblocks); 110*368c31abSDavid du Colombier dcache.write = MKNZ(DBlock*, nblocks); 111*368c31abSDavid du Colombier dcache.mem = MKNZ(u8int, (nblocks+1+128) * blocksize); 112*368c31abSDavid du Colombier dcache.ra = chancreate(sizeof(Ra), 0); 113*368c31abSDavid du Colombier 114*368c31abSDavid du Colombier last = nil; 115*368c31abSDavid du Colombier p = (u8int*)(((ulong)dcache.mem+blocksize-1)&~(ulong)(blocksize-1)); 116*368c31abSDavid du Colombier for(i = 0; i < nblocks; i++){ 117*368c31abSDavid du Colombier b = &dcache.blocks[i]; 118*368c31abSDavid du Colombier b->data = &p[i * blocksize]; 119*368c31abSDavid du Colombier b->heap = TWID32; 120*368c31abSDavid du Colombier b->writedonechan = chancreate(sizeof(void*), 1); 121*368c31abSDavid du Colombier b->next = last; 122*368c31abSDavid du Colombier last = b; 123*368c31abSDavid du Colombier } 124*368c31abSDavid du Colombier dcache.rabuf = &p[i*blocksize]; 125*368c31abSDavid du Colombier dcache.ramax = 128*blocksize; 126*368c31abSDavid du Colombier dcache.raaddr = 0; 127*368c31abSDavid du Colombier dcache.rapart = nil; 128*368c31abSDavid du Colombier 129*368c31abSDavid du Colombier dcache.free = last; 130*368c31abSDavid du Colombier dcache.nheap = 0; 131*368c31abSDavid du Colombier setstat(StatDcacheSize, nblocks); 132*368c31abSDavid du Colombier initround(&dcache.round, "dcache", 120*1000); 133*368c31abSDavid du Colombier 134*368c31abSDavid du Colombier vtproc(flushproc, nil); 135*368c31abSDavid du Colombier vtproc(delaykickroundproc, &dcache.round); 136*368c31abSDavid du Colombier vtproc(raproc, nil); 137*368c31abSDavid du Colombier } 138*368c31abSDavid du Colombier 139*368c31abSDavid du Colombier void 140*368c31abSDavid du Colombier setdcachestate(AState *a) 141*368c31abSDavid du Colombier { 142*368c31abSDavid du Colombier trace(TraceBlock, "setdcachestate %s 0x%llux clumps %d", a->arena ? a->arena->name : nil, a->aa, a->stats.clumps); 143*368c31abSDavid du Colombier qlock(&dcache.lock); 144*368c31abSDavid du Colombier dcache.state = *a; 145*368c31abSDavid du Colombier qunlock(&dcache.lock); 146*368c31abSDavid du Colombier } 147*368c31abSDavid du Colombier 148*368c31abSDavid du Colombier AState 149*368c31abSDavid du Colombier diskstate(void) 150*368c31abSDavid du Colombier { 151*368c31abSDavid du Colombier AState a; 152*368c31abSDavid du Colombier 153*368c31abSDavid du Colombier qlock(&dcache.lock); 154*368c31abSDavid du Colombier a = dcache.diskstate; 155*368c31abSDavid du Colombier qunlock(&dcache.lock); 156*368c31abSDavid du Colombier return a; 157*368c31abSDavid du Colombier } 158*368c31abSDavid du Colombier 159*368c31abSDavid du Colombier static void 160*368c31abSDavid du Colombier raproc(void *v) 161*368c31abSDavid du Colombier { 162*368c31abSDavid du Colombier Ra ra; 163*368c31abSDavid du Colombier DBlock *b; 164*368c31abSDavid du Colombier 165*368c31abSDavid du Colombier USED(v); 166*368c31abSDavid du Colombier while(recv(dcache.ra, &ra) == 1){ 167*368c31abSDavid du Colombier if(ra.part->size <= ra.addr) 168*368c31abSDavid du Colombier continue; 169*368c31abSDavid du Colombier b = _getdblock(ra.part, ra.addr, OREAD, 2); 170*368c31abSDavid du Colombier putdblock(b); 171*368c31abSDavid du Colombier } 172*368c31abSDavid du Colombier } 173*368c31abSDavid du Colombier 174*368c31abSDavid du Colombier /* 175*368c31abSDavid du Colombier * We do readahead a whole arena at a time now, 176*368c31abSDavid du Colombier * so dreadahead is a no-op. The original implementation 177*368c31abSDavid du Colombier * is in unused_dreadahead below. 178*368c31abSDavid du Colombier */ 179*368c31abSDavid du Colombier void 180*368c31abSDavid du Colombier dreadahead(Part *part, u64int addr, int miss) 181*368c31abSDavid du Colombier { 182*368c31abSDavid du Colombier USED(part); 183*368c31abSDavid du Colombier USED(addr); 184*368c31abSDavid du Colombier USED(miss); 185*368c31abSDavid du Colombier } 186*368c31abSDavid du Colombier 187*368c31abSDavid du Colombier void 188*368c31abSDavid du Colombier unused_dreadahead(Part *part, u64int addr, int miss) 189*368c31abSDavid du Colombier { 190*368c31abSDavid du Colombier Ra ra; 191*368c31abSDavid du Colombier static struct { 192*368c31abSDavid du Colombier Part *part; 193*368c31abSDavid du Colombier u64int addr; 194*368c31abSDavid du Colombier } lastmiss; 195*368c31abSDavid du Colombier static struct { 196*368c31abSDavid du Colombier Part *part; 197*368c31abSDavid du Colombier u64int addr; 198*368c31abSDavid du Colombier int dir; 199*368c31abSDavid du Colombier } lastra; 200*368c31abSDavid du Colombier 201*368c31abSDavid du Colombier if(miss){ 202*368c31abSDavid du Colombier if(lastmiss.part==part && lastmiss.addr==addr-dcache.size){ 203*368c31abSDavid du Colombier XRa: 204*368c31abSDavid du Colombier lastra.part = part; 205*368c31abSDavid du Colombier lastra.dir = addr-lastmiss.addr; 206*368c31abSDavid du Colombier lastra.addr = addr+lastra.dir; 207*368c31abSDavid du Colombier ra.part = part; 208*368c31abSDavid du Colombier ra.addr = lastra.addr; 209*368c31abSDavid du Colombier nbsend(dcache.ra, &ra); 210*368c31abSDavid du Colombier }else if(lastmiss.part==part && lastmiss.addr==addr+dcache.size){ 211*368c31abSDavid du Colombier addr -= dcache.size; 212*368c31abSDavid du Colombier goto XRa; 213*368c31abSDavid du Colombier } 214*368c31abSDavid du Colombier }else{ 215*368c31abSDavid du Colombier if(lastra.part==part && lastra.addr==addr){ 216*368c31abSDavid du Colombier lastra.addr += lastra.dir; 217*368c31abSDavid du Colombier ra.part = part; 218*368c31abSDavid du Colombier ra.addr = lastra.addr; 219*368c31abSDavid du Colombier nbsend(dcache.ra, &ra); 220*368c31abSDavid du Colombier } 221*368c31abSDavid du Colombier } 222*368c31abSDavid du Colombier 223*368c31abSDavid du Colombier if(miss){ 224*368c31abSDavid du Colombier lastmiss.part = part; 225*368c31abSDavid du Colombier lastmiss.addr = addr; 226*368c31abSDavid du Colombier } 227*368c31abSDavid du Colombier } 228*368c31abSDavid du Colombier 229*368c31abSDavid du Colombier int 230*368c31abSDavid du Colombier rareadpart(Part *part, u64int addr, u8int *buf, uint n, int load) 231*368c31abSDavid du Colombier { 232*368c31abSDavid du Colombier uint nn; 233*368c31abSDavid du Colombier static RWLock ralock; 234*368c31abSDavid du Colombier 235*368c31abSDavid du Colombier rlock(&ralock); 236*368c31abSDavid du Colombier if(dcache.rapart==part && dcache.raaddr <= addr && addr+n <= dcache.raaddr+dcache.rasize){ 237*368c31abSDavid du Colombier memmove(buf, dcache.rabuf+(addr-dcache.raaddr), n); 238*368c31abSDavid du Colombier runlock(&ralock); 239*368c31abSDavid du Colombier return 0; 240*368c31abSDavid du Colombier } 241*368c31abSDavid du Colombier if(load != 2 || addr >= part->size){ /* addr >= part->size: let readpart do the error */ 242*368c31abSDavid du Colombier runlock(&ralock); 243*368c31abSDavid du Colombier diskaccess(0); 244*368c31abSDavid du Colombier return readpart(part, addr, buf, n); 245*368c31abSDavid du Colombier } 246*368c31abSDavid du Colombier 247*368c31abSDavid du Colombier runlock(&ralock); 248*368c31abSDavid du Colombier wlock(&ralock); 249*368c31abSDavid du Colombier fprint(2, "raread %s %llx\n", part->name, addr); 250*368c31abSDavid du Colombier nn = dcache.ramax; 251*368c31abSDavid du Colombier if(addr+nn > part->size) 252*368c31abSDavid du Colombier nn = part->size - addr; 253*368c31abSDavid du Colombier diskaccess(0); 254*368c31abSDavid du Colombier if(readpart(part, addr, dcache.rabuf, nn) < 0){ 255*368c31abSDavid du Colombier wunlock(&ralock); 256*368c31abSDavid du Colombier return -1; 257*368c31abSDavid du Colombier } 258*368c31abSDavid du Colombier memmove(buf, dcache.rabuf, n); 259*368c31abSDavid du Colombier dcache.rapart = part; 260*368c31abSDavid du Colombier dcache.rasize = nn; 261*368c31abSDavid du Colombier dcache.raaddr = addr; 262*368c31abSDavid du Colombier wunlock(&ralock); 263*368c31abSDavid du Colombier 264*368c31abSDavid du Colombier addstat(StatApartReadBytes, nn-n); 265*368c31abSDavid du Colombier return 0; 266*368c31abSDavid du Colombier } 267*368c31abSDavid du Colombier 268*368c31abSDavid du Colombier static u32int 269*368c31abSDavid du Colombier pbhash(u64int addr) 270*368c31abSDavid du Colombier { 271*368c31abSDavid du Colombier u32int h; 272*368c31abSDavid du Colombier 273*368c31abSDavid du Colombier #define hashit(c) ((((c) * 0x6b43a9b5) >> (32 - HashLog)) & HashMask) 274*368c31abSDavid du Colombier h = (addr >> 32) ^ addr; 275*368c31abSDavid du Colombier return hashit(h); 276*368c31abSDavid du Colombier } 277*368c31abSDavid du Colombier 278*368c31abSDavid du Colombier DBlock* 279*368c31abSDavid du Colombier getdblock(Part *part, u64int addr, int mode) 280*368c31abSDavid du Colombier { 281*368c31abSDavid du Colombier DBlock *b; 282*368c31abSDavid du Colombier uint ms; 283*368c31abSDavid du Colombier 284*368c31abSDavid du Colombier ms = msec(); 285*368c31abSDavid du Colombier b = _getdblock(part, addr, mode, 1); 286*368c31abSDavid du Colombier if(mode == OREAD || mode == ORDWR) 287*368c31abSDavid du Colombier addstat(StatDcacheRead, 1); 288*368c31abSDavid du Colombier if(mode == OWRITE || mode == ORDWR) 289*368c31abSDavid du Colombier addstat(StatDcacheWrite, 1); 290*368c31abSDavid du Colombier ms = msec() - ms; 291*368c31abSDavid du Colombier addstat2(StatDcacheLookup, 1, StatDcacheLookupTime, ms); 292*368c31abSDavid du Colombier return b; 293*368c31abSDavid du Colombier } 294*368c31abSDavid du Colombier 295*368c31abSDavid du Colombier DBlock* 296*368c31abSDavid du Colombier _getdblock(Part *part, u64int addr, int mode, int load) 297*368c31abSDavid du Colombier { 298*368c31abSDavid du Colombier DBlock *b; 299*368c31abSDavid du Colombier u32int h, size; 300*368c31abSDavid du Colombier 301*368c31abSDavid du Colombier trace(TraceBlock, "getdblock enter %s 0x%llux", part->name, addr); 302*368c31abSDavid du Colombier size = part->blocksize; 303*368c31abSDavid du Colombier if(size > dcache.size){ 304*368c31abSDavid du Colombier seterr(EAdmin, "block size %d too big for cache with size %d", size, dcache.size); 305*368c31abSDavid du Colombier return nil; 306*368c31abSDavid du Colombier } 307*368c31abSDavid du Colombier h = pbhash(addr); 308*368c31abSDavid du Colombier 309*368c31abSDavid du Colombier /* 310*368c31abSDavid du Colombier * look for the block in the cache 311*368c31abSDavid du Colombier */ 312*368c31abSDavid du Colombier qlock(&dcache.lock); 313*368c31abSDavid du Colombier again: 314*368c31abSDavid du Colombier for(b = dcache.heads[h]; b != nil; b = b->next){ 315*368c31abSDavid du Colombier if(b->part == part && b->addr == addr){ 316*368c31abSDavid du Colombier /* 317*368c31abSDavid du Colombier qlock(&stats.lock); 318*368c31abSDavid du Colombier stats.pchit++; 319*368c31abSDavid du Colombier qunlock(&stats.lock); 320*368c31abSDavid du Colombier */ 321*368c31abSDavid du Colombier if(load){ 322*368c31abSDavid du Colombier addstat(StatDcacheHit, 1); 323*368c31abSDavid du Colombier if(load != 2 && mode != OWRITE) 324*368c31abSDavid du Colombier dreadahead(part, b->addr, 0); 325*368c31abSDavid du Colombier } 326*368c31abSDavid du Colombier goto found; 327*368c31abSDavid du Colombier } 328*368c31abSDavid du Colombier } 329*368c31abSDavid du Colombier 330*368c31abSDavid du Colombier /* 331*368c31abSDavid du Colombier * missed: locate the block with the oldest second to last use. 332*368c31abSDavid du Colombier * remove it from the heap, and fix up the heap. 333*368c31abSDavid du Colombier */ 334*368c31abSDavid du Colombier if(!load){ 335*368c31abSDavid du Colombier qunlock(&dcache.lock); 336*368c31abSDavid du Colombier return nil; 337*368c31abSDavid du Colombier } 338*368c31abSDavid du Colombier 339*368c31abSDavid du Colombier addstat(StatDcacheMiss, 1); 340*368c31abSDavid du Colombier 341*368c31abSDavid du Colombier b = bumpdblock(); 342*368c31abSDavid du Colombier if(b == nil){ 343*368c31abSDavid du Colombier trace(TraceBlock, "all disk cache blocks in use"); 344*368c31abSDavid du Colombier addstat(StatDcacheStall, 1); 345*368c31abSDavid du Colombier rsleep(&dcache.full); 346*368c31abSDavid du Colombier addstat(StatDcacheStall, -1); 347*368c31abSDavid du Colombier goto again; 348*368c31abSDavid du Colombier } 349*368c31abSDavid du Colombier 350*368c31abSDavid du Colombier assert(!b->dirty); 351*368c31abSDavid du Colombier 352*368c31abSDavid du Colombier /* 353*368c31abSDavid du Colombier * the new block has no last use, so assume it happens sometime in the middle 354*368c31abSDavid du Colombier ZZZ this is not reasonable 355*368c31abSDavid du Colombier */ 356*368c31abSDavid du Colombier b->used = (b->used2 + dcache.now) / 2; 357*368c31abSDavid du Colombier 358*368c31abSDavid du Colombier /* 359*368c31abSDavid du Colombier * rechain the block on the correct hash chain 360*368c31abSDavid du Colombier */ 361*368c31abSDavid du Colombier b->next = dcache.heads[h]; 362*368c31abSDavid du Colombier dcache.heads[h] = b; 363*368c31abSDavid du Colombier if(b->next != nil) 364*368c31abSDavid du Colombier b->next->prev = b; 365*368c31abSDavid du Colombier b->prev = nil; 366*368c31abSDavid du Colombier 367*368c31abSDavid du Colombier b->addr = addr; 368*368c31abSDavid du Colombier b->part = part; 369*368c31abSDavid du Colombier b->size = 0; 370*368c31abSDavid du Colombier if(load != 2 && mode != OWRITE) 371*368c31abSDavid du Colombier dreadahead(part, b->addr, 1); 372*368c31abSDavid du Colombier 373*368c31abSDavid du Colombier found: 374*368c31abSDavid du Colombier b->ref++; 375*368c31abSDavid du Colombier b->used2 = b->used; 376*368c31abSDavid du Colombier b->used = dcache.now++; 377*368c31abSDavid du Colombier if(b->heap != TWID32) 378*368c31abSDavid du Colombier fixheap(b->heap, b); 379*368c31abSDavid du Colombier 380*368c31abSDavid du Colombier qunlock(&dcache.lock); 381*368c31abSDavid du Colombier 382*368c31abSDavid du Colombier trace(TraceBlock, "getdblock lock"); 383*368c31abSDavid du Colombier addstat(StatDblockStall, 1); 384*368c31abSDavid du Colombier if(mode == OREAD) 385*368c31abSDavid du Colombier rlock(&b->lock); 386*368c31abSDavid du Colombier else 387*368c31abSDavid du Colombier wlock(&b->lock); 388*368c31abSDavid du Colombier addstat(StatDblockStall, -1); 389*368c31abSDavid du Colombier trace(TraceBlock, "getdblock locked"); 390*368c31abSDavid du Colombier 391*368c31abSDavid du Colombier if(b->size != size){ 392*368c31abSDavid du Colombier if(mode == OREAD){ 393*368c31abSDavid du Colombier addstat(StatDblockStall, 1); 394*368c31abSDavid du Colombier runlock(&b->lock); 395*368c31abSDavid du Colombier wlock(&b->lock); 396*368c31abSDavid du Colombier addstat(StatDblockStall, -1); 397*368c31abSDavid du Colombier } 398*368c31abSDavid du Colombier if(b->size < size){ 399*368c31abSDavid du Colombier if(mode == OWRITE) 400*368c31abSDavid du Colombier memset(&b->data[b->size], 0, size - b->size); 401*368c31abSDavid du Colombier else{ 402*368c31abSDavid du Colombier trace(TraceBlock, "getdblock readpart %s 0x%llux", part->name, addr); 403*368c31abSDavid du Colombier if(rareadpart(part, addr + b->size, &b->data[b->size], size - b->size, load) < 0){ 404*368c31abSDavid du Colombier b->mode = ORDWR; /* so putdblock wunlocks */ 405*368c31abSDavid du Colombier putdblock(b); 406*368c31abSDavid du Colombier return nil; 407*368c31abSDavid du Colombier } 408*368c31abSDavid du Colombier trace(TraceBlock, "getdblock readpartdone"); 409*368c31abSDavid du Colombier addstat(StatApartRead, 1); 410*368c31abSDavid du Colombier addstat(StatApartReadBytes, size-b->size); 411*368c31abSDavid du Colombier } 412*368c31abSDavid du Colombier } 413*368c31abSDavid du Colombier b->size = size; 414*368c31abSDavid du Colombier if(mode == OREAD){ 415*368c31abSDavid du Colombier addstat(StatDblockStall, 1); 416*368c31abSDavid du Colombier wunlock(&b->lock); 417*368c31abSDavid du Colombier rlock(&b->lock); 418*368c31abSDavid du Colombier addstat(StatDblockStall, -1); 419*368c31abSDavid du Colombier } 420*368c31abSDavid du Colombier } 421*368c31abSDavid du Colombier 422*368c31abSDavid du Colombier b->mode = mode; 423*368c31abSDavid du Colombier trace(TraceBlock, "getdblock exit"); 424*368c31abSDavid du Colombier return b; 425*368c31abSDavid du Colombier } 426*368c31abSDavid du Colombier 427*368c31abSDavid du Colombier void 428*368c31abSDavid du Colombier putdblock(DBlock *b) 429*368c31abSDavid du Colombier { 430*368c31abSDavid du Colombier if(b == nil) 431*368c31abSDavid du Colombier return; 432*368c31abSDavid du Colombier 433*368c31abSDavid du Colombier trace(TraceBlock, "putdblock %s 0x%llux", b->part->name, b->addr); 434*368c31abSDavid du Colombier 435*368c31abSDavid du Colombier if(b->mode == OREAD) 436*368c31abSDavid du Colombier runlock(&b->lock); 437*368c31abSDavid du Colombier else 438*368c31abSDavid du Colombier wunlock(&b->lock); 439*368c31abSDavid du Colombier 440*368c31abSDavid du Colombier qlock(&dcache.lock); 441*368c31abSDavid du Colombier if(--b->ref == 0 && !b->dirty){ 442*368c31abSDavid du Colombier if(b->heap == TWID32) 443*368c31abSDavid du Colombier upheap(dcache.nheap++, b); 444*368c31abSDavid du Colombier rwakeupall(&dcache.full); 445*368c31abSDavid du Colombier } 446*368c31abSDavid du Colombier qunlock(&dcache.lock); 447*368c31abSDavid du Colombier } 448*368c31abSDavid du Colombier 449*368c31abSDavid du Colombier void 450*368c31abSDavid du Colombier dirtydblock(DBlock *b, int dirty) 451*368c31abSDavid du Colombier { 452*368c31abSDavid du Colombier int odirty; 453*368c31abSDavid du Colombier Part *p; 454*368c31abSDavid du Colombier 455*368c31abSDavid du Colombier 456*368c31abSDavid du Colombier trace(TraceBlock, "dirtydblock enter %s 0x%llux %d from 0x%lux", b->part->name, b->addr, dirty, getcallerpc(&b)); 457*368c31abSDavid du Colombier assert(b->ref != 0); 458*368c31abSDavid du Colombier assert(b->mode==ORDWR || b->mode==OWRITE); 459*368c31abSDavid du Colombier 460*368c31abSDavid du Colombier odirty = b->dirty; 461*368c31abSDavid du Colombier if(b->dirty) 462*368c31abSDavid du Colombier assert(b->dirty == dirty); 463*368c31abSDavid du Colombier else 464*368c31abSDavid du Colombier b->dirty = dirty; 465*368c31abSDavid du Colombier 466*368c31abSDavid du Colombier p = b->part; 467*368c31abSDavid du Colombier if(p->writechan == nil){ 468*368c31abSDavid du Colombier trace(TraceBlock, "dirtydblock allocwriteproc %s", p->name); 469*368c31abSDavid du Colombier /* XXX hope this doesn't fail! */ 470*368c31abSDavid du Colombier p->writechan = chancreate(sizeof(DBlock*), dcache.nblocks); 471*368c31abSDavid du Colombier vtproc(writeproc, p); 472*368c31abSDavid du Colombier } 473*368c31abSDavid du Colombier qlock(&dcache.lock); 474*368c31abSDavid du Colombier if(!odirty){ 475*368c31abSDavid du Colombier dcache.ndirty++; 476*368c31abSDavid du Colombier setstat(StatDcacheDirty, dcache.ndirty); 477*368c31abSDavid du Colombier if(dcache.ndirty >= dcache.maxdirty) 478*368c31abSDavid du Colombier kickround(&dcache.round, 0); 479*368c31abSDavid du Colombier else 480*368c31abSDavid du Colombier delaykickround(&dcache.round); 481*368c31abSDavid du Colombier } 482*368c31abSDavid du Colombier qunlock(&dcache.lock); 483*368c31abSDavid du Colombier } 484*368c31abSDavid du Colombier 485*368c31abSDavid du Colombier static void 486*368c31abSDavid du Colombier unchain(DBlock *b) 487*368c31abSDavid du Colombier { 488*368c31abSDavid du Colombier ulong h; 489*368c31abSDavid du Colombier 490*368c31abSDavid du Colombier /* 491*368c31abSDavid du Colombier * unchain the block 492*368c31abSDavid du Colombier */ 493*368c31abSDavid du Colombier if(b->prev == nil){ 494*368c31abSDavid du Colombier h = pbhash(b->addr); 495*368c31abSDavid du Colombier if(dcache.heads[h] != b) 496*368c31abSDavid du Colombier sysfatal("bad hash chains in disk cache"); 497*368c31abSDavid du Colombier dcache.heads[h] = b->next; 498*368c31abSDavid du Colombier }else 499*368c31abSDavid du Colombier b->prev->next = b->next; 500*368c31abSDavid du Colombier if(b->next != nil) 501*368c31abSDavid du Colombier b->next->prev = b->prev; 502*368c31abSDavid du Colombier } 503*368c31abSDavid du Colombier 504*368c31abSDavid du Colombier /* 505*368c31abSDavid du Colombier * remove some block from use and update the free list and counters 506*368c31abSDavid du Colombier */ 507*368c31abSDavid du Colombier static DBlock* 508*368c31abSDavid du Colombier bumpdblock(void) 509*368c31abSDavid du Colombier { 510*368c31abSDavid du Colombier DBlock *b; 511*368c31abSDavid du Colombier 512*368c31abSDavid du Colombier trace(TraceBlock, "bumpdblock enter"); 513*368c31abSDavid du Colombier b = dcache.free; 514*368c31abSDavid du Colombier if(b != nil){ 515*368c31abSDavid du Colombier dcache.free = b->next; 516*368c31abSDavid du Colombier return b; 517*368c31abSDavid du Colombier } 518*368c31abSDavid du Colombier 519*368c31abSDavid du Colombier if(dcache.ndirty >= dcache.maxdirty) 520*368c31abSDavid du Colombier kickdcache(); 521*368c31abSDavid du Colombier 522*368c31abSDavid du Colombier /* 523*368c31abSDavid du Colombier * remove blocks until we find one that is unused 524*368c31abSDavid du Colombier * referenced blocks are left in the heap even though 525*368c31abSDavid du Colombier * they can't be scavenged; this is simple a speed optimization 526*368c31abSDavid du Colombier */ 527*368c31abSDavid du Colombier for(;;){ 528*368c31abSDavid du Colombier if(dcache.nheap == 0){ 529*368c31abSDavid du Colombier kickdcache(); 530*368c31abSDavid du Colombier trace(TraceBlock, "bumpdblock gotnothing"); 531*368c31abSDavid du Colombier return nil; 532*368c31abSDavid du Colombier } 533*368c31abSDavid du Colombier b = dcache.heap[0]; 534*368c31abSDavid du Colombier delheap(b); 535*368c31abSDavid du Colombier if(!b->ref && !b->dirty) 536*368c31abSDavid du Colombier break; 537*368c31abSDavid du Colombier } 538*368c31abSDavid du Colombier 539*368c31abSDavid du Colombier trace(TraceBlock, "bumpdblock bumping %s 0x%llux", b->part->name, b->addr); 540*368c31abSDavid du Colombier 541*368c31abSDavid du Colombier unchain(b); 542*368c31abSDavid du Colombier return b; 543*368c31abSDavid du Colombier } 544*368c31abSDavid du Colombier 545*368c31abSDavid du Colombier void 546*368c31abSDavid du Colombier emptydcache(void) 547*368c31abSDavid du Colombier { 548*368c31abSDavid du Colombier DBlock *b; 549*368c31abSDavid du Colombier 550*368c31abSDavid du Colombier qlock(&dcache.lock); 551*368c31abSDavid du Colombier while(dcache.nheap > 0){ 552*368c31abSDavid du Colombier b = dcache.heap[0]; 553*368c31abSDavid du Colombier delheap(b); 554*368c31abSDavid du Colombier if(!b->ref && !b->dirty){ 555*368c31abSDavid du Colombier unchain(b); 556*368c31abSDavid du Colombier b->next = dcache.free; 557*368c31abSDavid du Colombier dcache.free = b; 558*368c31abSDavid du Colombier } 559*368c31abSDavid du Colombier } 560*368c31abSDavid du Colombier qunlock(&dcache.lock); 561*368c31abSDavid du Colombier } 562*368c31abSDavid du Colombier 563*368c31abSDavid du Colombier /* 564*368c31abSDavid du Colombier * delete an arbitrary block from the heap 565*368c31abSDavid du Colombier */ 566*368c31abSDavid du Colombier static void 567*368c31abSDavid du Colombier delheap(DBlock *db) 568*368c31abSDavid du Colombier { 569*368c31abSDavid du Colombier if(db->heap == TWID32) 570*368c31abSDavid du Colombier return; 571*368c31abSDavid du Colombier fixheap(db->heap, dcache.heap[--dcache.nheap]); 572*368c31abSDavid du Colombier db->heap = TWID32; 573*368c31abSDavid du Colombier } 574*368c31abSDavid du Colombier 575*368c31abSDavid du Colombier /* 576*368c31abSDavid du Colombier * push an element up or down to it's correct new location 577*368c31abSDavid du Colombier */ 578*368c31abSDavid du Colombier static void 579*368c31abSDavid du Colombier fixheap(int i, DBlock *b) 580*368c31abSDavid du Colombier { 581*368c31abSDavid du Colombier if(upheap(i, b) == i) 582*368c31abSDavid du Colombier downheap(i, b); 583*368c31abSDavid du Colombier } 584*368c31abSDavid du Colombier 585*368c31abSDavid du Colombier static int 586*368c31abSDavid du Colombier upheap(int i, DBlock *b) 587*368c31abSDavid du Colombier { 588*368c31abSDavid du Colombier DBlock *bb; 589*368c31abSDavid du Colombier u32int now; 590*368c31abSDavid du Colombier int p; 591*368c31abSDavid du Colombier 592*368c31abSDavid du Colombier now = dcache.now; 593*368c31abSDavid du Colombier for(; i != 0; i = p){ 594*368c31abSDavid du Colombier p = (i - 1) >> 1; 595*368c31abSDavid du Colombier bb = dcache.heap[p]; 596*368c31abSDavid du Colombier if(b->used2 - now >= bb->used2 - now) 597*368c31abSDavid du Colombier break; 598*368c31abSDavid du Colombier dcache.heap[i] = bb; 599*368c31abSDavid du Colombier bb->heap = i; 600*368c31abSDavid du Colombier } 601*368c31abSDavid du Colombier 602*368c31abSDavid du Colombier dcache.heap[i] = b; 603*368c31abSDavid du Colombier b->heap = i; 604*368c31abSDavid du Colombier return i; 605*368c31abSDavid du Colombier } 606*368c31abSDavid du Colombier 607*368c31abSDavid du Colombier static int 608*368c31abSDavid du Colombier downheap(int i, DBlock *b) 609*368c31abSDavid du Colombier { 610*368c31abSDavid du Colombier DBlock *bb; 611*368c31abSDavid du Colombier u32int now; 612*368c31abSDavid du Colombier int k; 613*368c31abSDavid du Colombier 614*368c31abSDavid du Colombier now = dcache.now; 615*368c31abSDavid du Colombier for(; ; i = k){ 616*368c31abSDavid du Colombier k = (i << 1) + 1; 617*368c31abSDavid du Colombier if(k >= dcache.nheap) 618*368c31abSDavid du Colombier break; 619*368c31abSDavid du Colombier if(k + 1 < dcache.nheap && dcache.heap[k]->used2 - now > dcache.heap[k + 1]->used2 - now) 620*368c31abSDavid du Colombier k++; 621*368c31abSDavid du Colombier bb = dcache.heap[k]; 622*368c31abSDavid du Colombier if(b->used2 - now <= bb->used2 - now) 623*368c31abSDavid du Colombier break; 624*368c31abSDavid du Colombier dcache.heap[i] = bb; 625*368c31abSDavid du Colombier bb->heap = i; 626*368c31abSDavid du Colombier } 627*368c31abSDavid du Colombier 628*368c31abSDavid du Colombier dcache.heap[i] = b; 629*368c31abSDavid du Colombier b->heap = i; 630*368c31abSDavid du Colombier return i; 631*368c31abSDavid du Colombier } 632*368c31abSDavid du Colombier 633*368c31abSDavid du Colombier static void 634*368c31abSDavid du Colombier findblock(DBlock *bb) 635*368c31abSDavid du Colombier { 636*368c31abSDavid du Colombier DBlock *b, *last; 637*368c31abSDavid du Colombier int h; 638*368c31abSDavid du Colombier 639*368c31abSDavid du Colombier last = nil; 640*368c31abSDavid du Colombier h = pbhash(bb->addr); 641*368c31abSDavid du Colombier for(b = dcache.heads[h]; b != nil; b = b->next){ 642*368c31abSDavid du Colombier if(last != b->prev) 643*368c31abSDavid du Colombier sysfatal("bad prev link"); 644*368c31abSDavid du Colombier if(b == bb) 645*368c31abSDavid du Colombier return; 646*368c31abSDavid du Colombier last = b; 647*368c31abSDavid du Colombier } 648*368c31abSDavid du Colombier sysfatal("block missing from hash table"); 649*368c31abSDavid du Colombier } 650*368c31abSDavid du Colombier 651*368c31abSDavid du Colombier void 652*368c31abSDavid du Colombier checkdcache(void) 653*368c31abSDavid du Colombier { 654*368c31abSDavid du Colombier DBlock *b; 655*368c31abSDavid du Colombier u32int size, now; 656*368c31abSDavid du Colombier int i, k, refed, nfree; 657*368c31abSDavid du Colombier 658*368c31abSDavid du Colombier qlock(&dcache.lock); 659*368c31abSDavid du Colombier size = dcache.size; 660*368c31abSDavid du Colombier now = dcache.now; 661*368c31abSDavid du Colombier for(i = 0; i < dcache.nheap; i++){ 662*368c31abSDavid du Colombier if(dcache.heap[i]->heap != i) 663*368c31abSDavid du Colombier sysfatal("dc: mis-heaped at %d: %d", i, dcache.heap[i]->heap); 664*368c31abSDavid du Colombier if(i > 0 && dcache.heap[(i - 1) >> 1]->used2 - now > dcache.heap[i]->used2 - now) 665*368c31abSDavid du Colombier sysfatal("dc: bad heap ordering"); 666*368c31abSDavid du Colombier k = (i << 1) + 1; 667*368c31abSDavid du Colombier if(k < dcache.nheap && dcache.heap[i]->used2 - now > dcache.heap[k]->used2 - now) 668*368c31abSDavid du Colombier sysfatal("dc: bad heap ordering"); 669*368c31abSDavid du Colombier k++; 670*368c31abSDavid du Colombier if(k < dcache.nheap && dcache.heap[i]->used2 - now > dcache.heap[k]->used2 - now) 671*368c31abSDavid du Colombier sysfatal("dc: bad heap ordering"); 672*368c31abSDavid du Colombier } 673*368c31abSDavid du Colombier 674*368c31abSDavid du Colombier refed = 0; 675*368c31abSDavid du Colombier for(i = 0; i < dcache.nblocks; i++){ 676*368c31abSDavid du Colombier b = &dcache.blocks[i]; 677*368c31abSDavid du Colombier if(b->data != &dcache.mem[i * size]) 678*368c31abSDavid du Colombier sysfatal("dc: mis-blocked at %d", i); 679*368c31abSDavid du Colombier if(b->ref && b->heap == TWID32) 680*368c31abSDavid du Colombier refed++; 681*368c31abSDavid du Colombier if(b->addr) 682*368c31abSDavid du Colombier findblock(b); 683*368c31abSDavid du Colombier if(b->heap != TWID32 684*368c31abSDavid du Colombier && dcache.heap[b->heap] != b) 685*368c31abSDavid du Colombier sysfatal("dc: spurious heap value"); 686*368c31abSDavid du Colombier } 687*368c31abSDavid du Colombier 688*368c31abSDavid du Colombier nfree = 0; 689*368c31abSDavid du Colombier for(b = dcache.free; b != nil; b = b->next){ 690*368c31abSDavid du Colombier if(b->addr != 0 || b->heap != TWID32) 691*368c31abSDavid du Colombier sysfatal("dc: bad free list"); 692*368c31abSDavid du Colombier nfree++; 693*368c31abSDavid du Colombier } 694*368c31abSDavid du Colombier 695*368c31abSDavid du Colombier if(dcache.nheap + nfree + refed != dcache.nblocks) 696*368c31abSDavid du Colombier sysfatal("dc: missing blocks: %d %d %d", dcache.nheap, refed, dcache.nblocks); 697*368c31abSDavid du Colombier qunlock(&dcache.lock); 698*368c31abSDavid du Colombier } 699*368c31abSDavid du Colombier 700*368c31abSDavid du Colombier void 701*368c31abSDavid du Colombier flushdcache(void) 702*368c31abSDavid du Colombier { 703*368c31abSDavid du Colombier trace(TraceProc, "flushdcache enter"); 704*368c31abSDavid du Colombier kickround(&dcache.round, 1); 705*368c31abSDavid du Colombier trace(TraceProc, "flushdcache exit"); 706*368c31abSDavid du Colombier } 707*368c31abSDavid du Colombier 708*368c31abSDavid du Colombier void 709*368c31abSDavid du Colombier kickdcache(void) 710*368c31abSDavid du Colombier { 711*368c31abSDavid du Colombier kickround(&dcache.round, 0); 712*368c31abSDavid du Colombier } 713*368c31abSDavid du Colombier 714*368c31abSDavid du Colombier static int 715*368c31abSDavid du Colombier parallelwrites(DBlock **b, DBlock **eb, int dirty) 716*368c31abSDavid du Colombier { 717*368c31abSDavid du Colombier DBlock **p, **q; 718*368c31abSDavid du Colombier Part *part; 719*368c31abSDavid du Colombier 720*368c31abSDavid du Colombier for(p=b; p<eb && (*p)->dirty == dirty; p++){ 721*368c31abSDavid du Colombier assert(b<=p && p<eb); 722*368c31abSDavid du Colombier sendp((*p)->part->writechan, *p); 723*368c31abSDavid du Colombier } 724*368c31abSDavid du Colombier q = p; 725*368c31abSDavid du Colombier for(p=b; p<q; p++){ 726*368c31abSDavid du Colombier assert(b<=p && p<eb); 727*368c31abSDavid du Colombier recvp((*p)->writedonechan); 728*368c31abSDavid du Colombier } 729*368c31abSDavid du Colombier 730*368c31abSDavid du Colombier /* 731*368c31abSDavid du Colombier * Flush the partitions that have been written to. 732*368c31abSDavid du Colombier */ 733*368c31abSDavid du Colombier part = nil; 734*368c31abSDavid du Colombier for(p=b; p<q; p++){ 735*368c31abSDavid du Colombier if(part != (*p)->part){ 736*368c31abSDavid du Colombier part = (*p)->part; 737*368c31abSDavid du Colombier flushpart(part); /* what if it fails? */ 738*368c31abSDavid du Colombier } 739*368c31abSDavid du Colombier } 740*368c31abSDavid du Colombier 741*368c31abSDavid du Colombier return p-b; 742*368c31abSDavid du Colombier } 743*368c31abSDavid du Colombier 744*368c31abSDavid du Colombier /* 745*368c31abSDavid du Colombier * Sort first by dirty flag, then by partition, then by address in partition. 746*368c31abSDavid du Colombier */ 747*368c31abSDavid du Colombier static int 748*368c31abSDavid du Colombier writeblockcmp(const void *va, const void *vb) 749*368c31abSDavid du Colombier { 750*368c31abSDavid du Colombier DBlock *a, *b; 751*368c31abSDavid du Colombier 752*368c31abSDavid du Colombier a = *(DBlock**)va; 753*368c31abSDavid du Colombier b = *(DBlock**)vb; 754*368c31abSDavid du Colombier 755*368c31abSDavid du Colombier if(a->dirty != b->dirty) 756*368c31abSDavid du Colombier return a->dirty - b->dirty; 757*368c31abSDavid du Colombier if(a->part != b->part){ 758*368c31abSDavid du Colombier if(a->part < b->part) 759*368c31abSDavid du Colombier return -1; 760*368c31abSDavid du Colombier if(a->part > b->part) 761*368c31abSDavid du Colombier return 1; 762*368c31abSDavid du Colombier } 763*368c31abSDavid du Colombier if(a->addr < b->addr) 764*368c31abSDavid du Colombier return -1; 765*368c31abSDavid du Colombier return 1; 766*368c31abSDavid du Colombier } 767*368c31abSDavid du Colombier 768*368c31abSDavid du Colombier static void 769*368c31abSDavid du Colombier flushproc(void *v) 770*368c31abSDavid du Colombier { 771*368c31abSDavid du Colombier int i, j, n; 772*368c31abSDavid du Colombier ulong t0; 773*368c31abSDavid du Colombier DBlock *b, **write; 774*368c31abSDavid du Colombier AState as; 775*368c31abSDavid du Colombier 776*368c31abSDavid du Colombier USED(v); 777*368c31abSDavid du Colombier threadsetname("flushproc"); 778*368c31abSDavid du Colombier for(;;){ 779*368c31abSDavid du Colombier waitforkick(&dcache.round); 780*368c31abSDavid du Colombier 781*368c31abSDavid du Colombier trace(TraceWork, "start"); 782*368c31abSDavid du Colombier qlock(&dcache.lock); 783*368c31abSDavid du Colombier as = dcache.state; 784*368c31abSDavid du Colombier qunlock(&dcache.lock); 785*368c31abSDavid du Colombier 786*368c31abSDavid du Colombier t0 = nsec()/1000; 787*368c31abSDavid du Colombier 788*368c31abSDavid du Colombier trace(TraceProc, "build t=%lud", (ulong)(nsec()/1000)-t0); 789*368c31abSDavid du Colombier write = dcache.write; 790*368c31abSDavid du Colombier n = 0; 791*368c31abSDavid du Colombier for(i=0; i<dcache.nblocks; i++){ 792*368c31abSDavid du Colombier b = &dcache.blocks[i]; 793*368c31abSDavid du Colombier if(b->dirty) 794*368c31abSDavid du Colombier write[n++] = b; 795*368c31abSDavid du Colombier } 796*368c31abSDavid du Colombier 797*368c31abSDavid du Colombier qsort(write, n, sizeof(write[0]), writeblockcmp); 798*368c31abSDavid du Colombier 799*368c31abSDavid du Colombier /* Write each stage of blocks out. */ 800*368c31abSDavid du Colombier trace(TraceProc, "writeblocks t=%lud", (ulong)(nsec()/1000)-t0); 801*368c31abSDavid du Colombier i = 0; 802*368c31abSDavid du Colombier for(j=1; j<DirtyMax; j++){ 803*368c31abSDavid du Colombier trace(TraceProc, "writeblocks.%d t=%lud", j, (ulong)(nsec()/1000)-t0); 804*368c31abSDavid du Colombier i += parallelwrites(write+i, write+n, j); 805*368c31abSDavid du Colombier } 806*368c31abSDavid du Colombier if(i != n){ 807*368c31abSDavid du Colombier fprint(2, "in flushproc i=%d n=%d\n", i, n); 808*368c31abSDavid du Colombier for(i=0; i<n; i++) 809*368c31abSDavid du Colombier fprint(2, "\tblock %d: dirty=%d\n", i, write[i]->dirty); 810*368c31abSDavid du Colombier abort(); 811*368c31abSDavid du Colombier } 812*368c31abSDavid du Colombier 813*368c31abSDavid du Colombier /* XXX 814*368c31abSDavid du Colombier * the locking here is suspect. what if a block is redirtied 815*368c31abSDavid du Colombier * after the write happens? we'll still decrement dcache.ndirty here. 816*368c31abSDavid du Colombier */ 817*368c31abSDavid du Colombier trace(TraceProc, "undirty.%d t=%lud", j, (ulong)(nsec()/1000)-t0); 818*368c31abSDavid du Colombier qlock(&dcache.lock); 819*368c31abSDavid du Colombier dcache.diskstate = as; 820*368c31abSDavid du Colombier for(i=0; i<n; i++){ 821*368c31abSDavid du Colombier b = write[i]; 822*368c31abSDavid du Colombier --dcache.ndirty; 823*368c31abSDavid du Colombier if(b->ref == 0 && b->heap == TWID32){ 824*368c31abSDavid du Colombier upheap(dcache.nheap++, b); 825*368c31abSDavid du Colombier rwakeupall(&dcache.full); 826*368c31abSDavid du Colombier } 827*368c31abSDavid du Colombier } 828*368c31abSDavid du Colombier setstat(StatDcacheDirty, dcache.ndirty); 829*368c31abSDavid du Colombier qunlock(&dcache.lock); 830*368c31abSDavid du Colombier addstat(StatDcacheFlush, 1); 831*368c31abSDavid du Colombier trace(TraceWork, "finish"); 832*368c31abSDavid du Colombier } 833*368c31abSDavid du Colombier } 834*368c31abSDavid du Colombier 835*368c31abSDavid du Colombier static void 836*368c31abSDavid du Colombier writeproc(void *v) 837*368c31abSDavid du Colombier { 838*368c31abSDavid du Colombier DBlock *b; 839*368c31abSDavid du Colombier Part *p; 840*368c31abSDavid du Colombier 841*368c31abSDavid du Colombier p = v; 842*368c31abSDavid du Colombier 843*368c31abSDavid du Colombier threadsetname("writeproc:%s", p->name); 844*368c31abSDavid du Colombier for(;;){ 845*368c31abSDavid du Colombier b = recvp(p->writechan); 846*368c31abSDavid du Colombier trace(TraceWork, "start"); 847*368c31abSDavid du Colombier assert(b->part == p); 848*368c31abSDavid du Colombier trace(TraceProc, "wlock %s 0x%llux", p->name, b->addr); 849*368c31abSDavid du Colombier wlock(&b->lock); 850*368c31abSDavid du Colombier trace(TraceProc, "writepart %s 0x%llux", p->name, b->addr); 851*368c31abSDavid du Colombier diskaccess(0); 852*368c31abSDavid du Colombier if(writepart(p, b->addr, b->data, b->size) < 0) 853*368c31abSDavid du Colombier fprint(2, "write error: %r\n"); /* XXX details! */ 854*368c31abSDavid du Colombier addstat(StatApartWrite, 1); 855*368c31abSDavid du Colombier addstat(StatApartWriteBytes, b->size); 856*368c31abSDavid du Colombier b->dirty = 0; 857*368c31abSDavid du Colombier wunlock(&b->lock); 858*368c31abSDavid du Colombier trace(TraceProc, "finish %s 0x%llux", p->name, b->addr); 859*368c31abSDavid du Colombier trace(TraceWork, "finish"); 860*368c31abSDavid du Colombier sendp(b->writedonechan, b); 861*368c31abSDavid du Colombier } 862*368c31abSDavid du Colombier } 863