15e96a66cSDavid du Colombier #include "stdinc.h" 25e96a66cSDavid du Colombier #include "dat.h" 35e96a66cSDavid du Colombier #include "fns.h" 45e96a66cSDavid du Colombier #include "error.h" 55e96a66cSDavid du Colombier 65e96a66cSDavid du Colombier static void diskThread(void *a); 75e96a66cSDavid du Colombier 85e96a66cSDavid du Colombier enum { 95e96a66cSDavid du Colombier QueueSize = 100, /* maximum block to queue */ 105e96a66cSDavid du Colombier }; 115e96a66cSDavid du Colombier 125e96a66cSDavid du Colombier struct Disk { 135e96a66cSDavid du Colombier VtLock *lk; 145e96a66cSDavid du Colombier int ref; 155e96a66cSDavid du Colombier 165e96a66cSDavid du Colombier int fd; 175e96a66cSDavid du Colombier Header h; 185e96a66cSDavid du Colombier 195e96a66cSDavid du Colombier VtRendez *flow; 205e96a66cSDavid du Colombier VtRendez *starve; 215e96a66cSDavid du Colombier VtRendez *flush; 225e96a66cSDavid du Colombier VtRendez *die; 235e96a66cSDavid du Colombier 245e96a66cSDavid du Colombier int nqueue; 255e96a66cSDavid du Colombier 265e96a66cSDavid du Colombier Block *cur; /* block to do on current scan */ 275e96a66cSDavid du Colombier Block *next; /* blocks to do next scan */ 285e96a66cSDavid du Colombier }; 295e96a66cSDavid du Colombier 30*f6333ca0SDavid du Colombier /* keep in sync with Part* enum in dat.h */ 31*f6333ca0SDavid du Colombier static char *partname[] = { 32*f6333ca0SDavid du Colombier [PartError] "error", 33*f6333ca0SDavid du Colombier [PartSuper] "super", 34*f6333ca0SDavid du Colombier [PartLabel] "label", 35*f6333ca0SDavid du Colombier [PartData] "data", 36*f6333ca0SDavid du Colombier [PartVenti] "venti", 37*f6333ca0SDavid du Colombier }; 385e96a66cSDavid du Colombier 395e96a66cSDavid du Colombier Disk * 405e96a66cSDavid du Colombier diskAlloc(int fd) 415e96a66cSDavid du Colombier { 425e96a66cSDavid du Colombier u8int buf[HeaderSize]; 435e96a66cSDavid du Colombier Header h; 445e96a66cSDavid du Colombier Disk *disk; 455e96a66cSDavid du Colombier 465e96a66cSDavid du Colombier if(pread(fd, buf, HeaderSize, HeaderOffset) < HeaderSize){ 47dc5a79c1SDavid du Colombier vtSetError("short read: %r"); 485e96a66cSDavid du Colombier vtOSError(); 495e96a66cSDavid du Colombier return nil; 505e96a66cSDavid du Colombier } 515e96a66cSDavid du Colombier 52dc5a79c1SDavid du Colombier if(!headerUnpack(&h, buf)){ 53dc5a79c1SDavid du Colombier vtSetError("bad disk header"); 545e96a66cSDavid du Colombier return nil; 55dc5a79c1SDavid du Colombier } 565e96a66cSDavid du Colombier disk = vtMemAllocZ(sizeof(Disk)); 575e96a66cSDavid du Colombier disk->lk = vtLockAlloc(); 585e96a66cSDavid du Colombier disk->starve = vtRendezAlloc(disk->lk); 595e96a66cSDavid du Colombier disk->flow = vtRendezAlloc(disk->lk); 605e96a66cSDavid du Colombier disk->flush = vtRendezAlloc(disk->lk); 615e96a66cSDavid du Colombier disk->fd = fd; 625e96a66cSDavid du Colombier disk->h = h; 635e96a66cSDavid du Colombier 645e96a66cSDavid du Colombier disk->ref = 2; 655e96a66cSDavid du Colombier vtThread(diskThread, disk); 665e96a66cSDavid du Colombier 675e96a66cSDavid du Colombier return disk; 685e96a66cSDavid du Colombier } 695e96a66cSDavid du Colombier 705e96a66cSDavid du Colombier void 715e96a66cSDavid du Colombier diskFree(Disk *disk) 725e96a66cSDavid du Colombier { 735e96a66cSDavid du Colombier diskFlush(disk); 745e96a66cSDavid du Colombier 755e96a66cSDavid du Colombier /* kill slave */ 765e96a66cSDavid du Colombier vtLock(disk->lk); 775e96a66cSDavid du Colombier disk->die = vtRendezAlloc(disk->lk); 785e96a66cSDavid du Colombier vtWakeup(disk->starve); 795e96a66cSDavid du Colombier while(disk->ref > 1) 805e96a66cSDavid du Colombier vtSleep(disk->die); 815e96a66cSDavid du Colombier vtUnlock(disk->lk); 825e96a66cSDavid du Colombier vtRendezFree(disk->flow); 835e96a66cSDavid du Colombier vtRendezFree(disk->starve); 845e96a66cSDavid du Colombier vtRendezFree(disk->die); 855e96a66cSDavid du Colombier vtLockFree(disk->lk); 865e96a66cSDavid du Colombier close(disk->fd); 875e96a66cSDavid du Colombier vtMemFree(disk); 885e96a66cSDavid du Colombier } 895e96a66cSDavid du Colombier 905e96a66cSDavid du Colombier static u32int 915e96a66cSDavid du Colombier partStart(Disk *disk, int part) 925e96a66cSDavid du Colombier { 935e96a66cSDavid du Colombier switch(part){ 945e96a66cSDavid du Colombier default: 955e96a66cSDavid du Colombier assert(0); 965e96a66cSDavid du Colombier case PartSuper: 975e96a66cSDavid du Colombier return disk->h.super; 985e96a66cSDavid du Colombier case PartLabel: 995e96a66cSDavid du Colombier return disk->h.label; 1005e96a66cSDavid du Colombier case PartData: 1015e96a66cSDavid du Colombier return disk->h.data; 1025e96a66cSDavid du Colombier } 1035e96a66cSDavid du Colombier } 1045e96a66cSDavid du Colombier 1055e96a66cSDavid du Colombier 1065e96a66cSDavid du Colombier static u32int 1075e96a66cSDavid du Colombier partEnd(Disk *disk, int part) 1085e96a66cSDavid du Colombier { 1095e96a66cSDavid du Colombier switch(part){ 1105e96a66cSDavid du Colombier default: 1115e96a66cSDavid du Colombier assert(0); 1125e96a66cSDavid du Colombier case PartSuper: 1135e96a66cSDavid du Colombier return disk->h.super+1; 1145e96a66cSDavid du Colombier case PartLabel: 1155e96a66cSDavid du Colombier return disk->h.data; 1165e96a66cSDavid du Colombier case PartData: 1175e96a66cSDavid du Colombier return disk->h.end; 1185e96a66cSDavid du Colombier } 1195e96a66cSDavid du Colombier } 1205e96a66cSDavid du Colombier 1215e96a66cSDavid du Colombier int 1225e96a66cSDavid du Colombier diskReadRaw(Disk *disk, int part, u32int addr, uchar *buf) 1235e96a66cSDavid du Colombier { 1245e96a66cSDavid du Colombier ulong start, end; 1255e96a66cSDavid du Colombier u64int offset; 1265e96a66cSDavid du Colombier int n, nn; 1275e96a66cSDavid du Colombier 1285e96a66cSDavid du Colombier start = partStart(disk, part); 1295e96a66cSDavid du Colombier end = partEnd(disk, part); 1305e96a66cSDavid du Colombier 1315e96a66cSDavid du Colombier if(addr >= end-start){ 1325e96a66cSDavid du Colombier vtSetError(EBadAddr); 1335e96a66cSDavid du Colombier return 0; 1345e96a66cSDavid du Colombier } 1355e96a66cSDavid du Colombier 1365e96a66cSDavid du Colombier offset = ((u64int)(addr + start))*disk->h.blockSize; 1375e96a66cSDavid du Colombier n = disk->h.blockSize; 1385e96a66cSDavid du Colombier while(n > 0){ 1395e96a66cSDavid du Colombier nn = pread(disk->fd, buf, n, offset); 1405e96a66cSDavid du Colombier if(nn < 0){ 1415e96a66cSDavid du Colombier vtOSError(); 1425e96a66cSDavid du Colombier return 0; 1435e96a66cSDavid du Colombier } 1445e96a66cSDavid du Colombier if(nn == 0){ 145e569ccb5SDavid du Colombier vtSetError("eof reading disk"); 1465e96a66cSDavid du Colombier return 0; 1475e96a66cSDavid du Colombier } 1485e96a66cSDavid du Colombier n -= nn; 1495e96a66cSDavid du Colombier offset += nn; 1505e96a66cSDavid du Colombier buf += nn; 1515e96a66cSDavid du Colombier } 1525e96a66cSDavid du Colombier return 1; 1535e96a66cSDavid du Colombier } 1545e96a66cSDavid du Colombier 1555e96a66cSDavid du Colombier int 1565e96a66cSDavid du Colombier diskWriteRaw(Disk *disk, int part, u32int addr, uchar *buf) 1575e96a66cSDavid du Colombier { 1585e96a66cSDavid du Colombier ulong start, end; 1595e96a66cSDavid du Colombier u64int offset; 1605e96a66cSDavid du Colombier int n; 1615e96a66cSDavid du Colombier 1625e96a66cSDavid du Colombier start = partStart(disk, part); 1635e96a66cSDavid du Colombier end = partEnd(disk, part); 1645e96a66cSDavid du Colombier 1655e96a66cSDavid du Colombier if(addr >= end-start){ 1665e96a66cSDavid du Colombier vtSetError(EBadAddr); 1675e96a66cSDavid du Colombier return 0; 1685e96a66cSDavid du Colombier } 1695e96a66cSDavid du Colombier 1705e96a66cSDavid du Colombier offset = ((u64int)(addr + start))*disk->h.blockSize; 1716de6ce84SDavid du Colombier n = pwrite(disk->fd, buf, disk->h.blockSize, offset); 1726de6ce84SDavid du Colombier if(n < 0){ 1735e96a66cSDavid du Colombier vtOSError(); 1745e96a66cSDavid du Colombier return 0; 1755e96a66cSDavid du Colombier } 1766de6ce84SDavid du Colombier if(n < disk->h.blockSize) { 1776de6ce84SDavid du Colombier vtSetError("short write"); 1786de6ce84SDavid du Colombier return 0; 1796de6ce84SDavid du Colombier } 1805e96a66cSDavid du Colombier 1815e96a66cSDavid du Colombier return 1; 1825e96a66cSDavid du Colombier } 1835e96a66cSDavid du Colombier 1845e96a66cSDavid du Colombier static void 1855e96a66cSDavid du Colombier diskQueue(Disk *disk, Block *b) 1865e96a66cSDavid du Colombier { 1875e96a66cSDavid du Colombier Block **bp, *bb; 1885e96a66cSDavid du Colombier 1895e96a66cSDavid du Colombier vtLock(disk->lk); 1905e96a66cSDavid du Colombier while(disk->nqueue >= QueueSize) 1915e96a66cSDavid du Colombier vtSleep(disk->flow); 1925e96a66cSDavid du Colombier if(disk->cur == nil || b->addr > disk->cur->addr) 1935e96a66cSDavid du Colombier bp = &disk->cur; 1945e96a66cSDavid du Colombier else 1955e96a66cSDavid du Colombier bp = &disk->next; 1965e96a66cSDavid du Colombier 1975e96a66cSDavid du Colombier for(bb=*bp; bb; bb=*bp){ 1985e96a66cSDavid du Colombier if(b->addr < bb->addr) 1995e96a66cSDavid du Colombier break; 2005e96a66cSDavid du Colombier bp = &bb->ionext; 2015e96a66cSDavid du Colombier } 2025e96a66cSDavid du Colombier b->ionext = bb; 2035e96a66cSDavid du Colombier *bp = b; 2045e96a66cSDavid du Colombier if(disk->nqueue == 0) 2055e96a66cSDavid du Colombier vtWakeup(disk->starve); 2065e96a66cSDavid du Colombier disk->nqueue++; 2075e96a66cSDavid du Colombier vtUnlock(disk->lk); 2085e96a66cSDavid du Colombier } 2095e96a66cSDavid du Colombier 2105e96a66cSDavid du Colombier 2115e96a66cSDavid du Colombier void 2125e96a66cSDavid du Colombier diskRead(Disk *disk, Block *b) 2135e96a66cSDavid du Colombier { 2145e96a66cSDavid du Colombier assert(b->iostate == BioEmpty || b->iostate == BioLabel); 2155e96a66cSDavid du Colombier blockSetIOState(b, BioReading); 2165e96a66cSDavid du Colombier diskQueue(disk, b); 2175e96a66cSDavid du Colombier } 2185e96a66cSDavid du Colombier 2195e96a66cSDavid du Colombier void 2205e96a66cSDavid du Colombier diskWrite(Disk *disk, Block *b) 2215e96a66cSDavid du Colombier { 222867bfcc6SDavid du Colombier assert(b->nlock == 1); 2235e96a66cSDavid du Colombier assert(b->iostate == BioDirty); 2245e96a66cSDavid du Colombier blockSetIOState(b, BioWriting); 2255e96a66cSDavid du Colombier diskQueue(disk, b); 2265e96a66cSDavid du Colombier } 2275e96a66cSDavid du Colombier 228867bfcc6SDavid du Colombier void 229867bfcc6SDavid du Colombier diskWriteAndWait(Disk *disk, Block *b) 230867bfcc6SDavid du Colombier { 231867bfcc6SDavid du Colombier int nlock; 232867bfcc6SDavid du Colombier 233867bfcc6SDavid du Colombier /* 234867bfcc6SDavid du Colombier * If b->nlock > 1, the block is aliased within 235867bfcc6SDavid du Colombier * a single thread. That thread is us. 236867bfcc6SDavid du Colombier * DiskWrite does some funny stuff with VtLock 237867bfcc6SDavid du Colombier * and blockPut that basically assumes b->nlock==1. 238867bfcc6SDavid du Colombier * We humor diskWrite by temporarily setting 239867bfcc6SDavid du Colombier * nlock to 1. This needs to be revisited. 240867bfcc6SDavid du Colombier */ 241867bfcc6SDavid du Colombier nlock = b->nlock; 242867bfcc6SDavid du Colombier if(nlock > 1) 243867bfcc6SDavid du Colombier b->nlock = 1; 244867bfcc6SDavid du Colombier diskWrite(disk, b); 245867bfcc6SDavid du Colombier while(b->iostate != BioClean) 246867bfcc6SDavid du Colombier vtSleep(b->ioready); 247867bfcc6SDavid du Colombier b->nlock = nlock; 248867bfcc6SDavid du Colombier } 249867bfcc6SDavid du Colombier 2505e96a66cSDavid du Colombier int 2515e96a66cSDavid du Colombier diskBlockSize(Disk *disk) 2525e96a66cSDavid du Colombier { 2535e96a66cSDavid du Colombier return disk->h.blockSize; /* immuttable */ 2545e96a66cSDavid du Colombier } 2555e96a66cSDavid du Colombier 2565e96a66cSDavid du Colombier int 2575e96a66cSDavid du Colombier diskFlush(Disk *disk) 2585e96a66cSDavid du Colombier { 2595e96a66cSDavid du Colombier Dir dir; 2605e96a66cSDavid du Colombier 2615e96a66cSDavid du Colombier vtLock(disk->lk); 2625e96a66cSDavid du Colombier while(disk->nqueue > 0) 2635e96a66cSDavid du Colombier vtSleep(disk->flush); 2645e96a66cSDavid du Colombier vtUnlock(disk->lk); 2655e96a66cSDavid du Colombier 2665e96a66cSDavid du Colombier /* there really should be a cleaner interface to flush an fd */ 2675e96a66cSDavid du Colombier nulldir(&dir); 2685e96a66cSDavid du Colombier if(dirfwstat(disk->fd, &dir) < 0){ 2695e96a66cSDavid du Colombier vtOSError(); 2705e96a66cSDavid du Colombier return 0; 2715e96a66cSDavid du Colombier } 2725e96a66cSDavid du Colombier return 1; 2735e96a66cSDavid du Colombier } 2745e96a66cSDavid du Colombier 2755e96a66cSDavid du Colombier u32int 2765e96a66cSDavid du Colombier diskSize(Disk *disk, int part) 2775e96a66cSDavid du Colombier { 2785e96a66cSDavid du Colombier return partEnd(disk, part) - partStart(disk, part); 2795e96a66cSDavid du Colombier } 2805e96a66cSDavid du Colombier 28174f16c81SDavid du Colombier static uintptr 282fe853e23SDavid du Colombier mypc(int x) 283fe853e23SDavid du Colombier { 284fe853e23SDavid du Colombier return getcallerpc(&x); 285fe853e23SDavid du Colombier } 286fe853e23SDavid du Colombier 287*f6333ca0SDavid du Colombier static char * 288*f6333ca0SDavid du Colombier disk2file(Disk *disk) 289*f6333ca0SDavid du Colombier { 290*f6333ca0SDavid du Colombier static char buf[256]; 291*f6333ca0SDavid du Colombier 292*f6333ca0SDavid du Colombier if (fd2path(disk->fd, buf, sizeof buf) < 0) 293*f6333ca0SDavid du Colombier strncpy(buf, "GOK", sizeof buf); 294*f6333ca0SDavid du Colombier return buf; 295*f6333ca0SDavid du Colombier } 296*f6333ca0SDavid du Colombier 2975e96a66cSDavid du Colombier static void 2985e96a66cSDavid du Colombier diskThread(void *a) 2995e96a66cSDavid du Colombier { 3005e96a66cSDavid du Colombier Disk *disk = a; 3015e96a66cSDavid du Colombier Block *b; 3025e96a66cSDavid du Colombier uchar *buf, *p; 3035e96a66cSDavid du Colombier double t; 3045e96a66cSDavid du Colombier int nio; 3055e96a66cSDavid du Colombier 3065e96a66cSDavid du Colombier vtThreadSetName("disk"); 3075e96a66cSDavid du Colombier 308dc5a79c1SDavid du Colombier //fprint(2, "diskThread %d\n", getpid()); 3095e96a66cSDavid du Colombier 3105e96a66cSDavid du Colombier buf = vtMemAlloc(disk->h.blockSize); 3115e96a66cSDavid du Colombier 3125e96a66cSDavid du Colombier vtLock(disk->lk); 3135e96a66cSDavid du Colombier nio = 0; 3145e96a66cSDavid du Colombier t = -nsec(); 3155e96a66cSDavid du Colombier for(;;){ 3165e96a66cSDavid du Colombier while(disk->nqueue == 0){ 3175e96a66cSDavid du Colombier t += nsec(); 31839734e7eSDavid du Colombier //if(nio >= 10000){ 31939734e7eSDavid du Colombier //fprint(2, "disk: io=%d at %.3fms\n", nio, t*1e-6/nio); 32039734e7eSDavid du Colombier //nio = 0; 32139734e7eSDavid du Colombier //t = 0.; 32239734e7eSDavid du Colombier //} 3235e96a66cSDavid du Colombier if(disk->die != nil) 3245e96a66cSDavid du Colombier goto Done; 3255e96a66cSDavid du Colombier vtSleep(disk->starve); 3265e96a66cSDavid du Colombier t -= nsec(); 3275e96a66cSDavid du Colombier } 3285e96a66cSDavid du Colombier assert(disk->cur != nil || disk->next != nil); 3295e96a66cSDavid du Colombier 3305e96a66cSDavid du Colombier if(disk->cur == nil){ 3315e96a66cSDavid du Colombier disk->cur = disk->next; 3325e96a66cSDavid du Colombier disk->next = nil; 3335e96a66cSDavid du Colombier } 3345e96a66cSDavid du Colombier b = disk->cur; 3355e96a66cSDavid du Colombier disk->cur = b->ionext; 3365e96a66cSDavid du Colombier vtUnlock(disk->lk); 3375e96a66cSDavid du Colombier 3385e96a66cSDavid du Colombier /* 3395e96a66cSDavid du Colombier * no one should hold onto blocking in the 3405e96a66cSDavid du Colombier * reading or writing state, so this lock should 3415e96a66cSDavid du Colombier * not cause deadlock. 3425e96a66cSDavid du Colombier */ 343*f6333ca0SDavid du Colombier if(0)fprint(2, "fossil: diskThread: %d:%d %x\n", getpid(), b->part, b->addr); 3445e96a66cSDavid du Colombier bwatchLock(b); 3455e96a66cSDavid du Colombier vtLock(b->lk); 346fe853e23SDavid du Colombier b->pc = mypc(0); 3475e96a66cSDavid du Colombier assert(b->nlock == 1); 3485e96a66cSDavid du Colombier switch(b->iostate){ 3495e96a66cSDavid du Colombier default: 3505e96a66cSDavid du Colombier abort(); 3515e96a66cSDavid du Colombier case BioReading: 3525e96a66cSDavid du Colombier if(!diskReadRaw(disk, b->part, b->addr, b->data)){ 353*f6333ca0SDavid du Colombier fprint(2, "fossil: diskReadRaw failed: %s: score %V: part=%s addr=%ud: %r\n", 354*f6333ca0SDavid du Colombier disk2file(disk), b->score, partname[b->part], b->addr); 3555e96a66cSDavid du Colombier blockSetIOState(b, BioReadError); 3565e96a66cSDavid du Colombier }else 3575e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 3585e96a66cSDavid du Colombier break; 3595e96a66cSDavid du Colombier case BioWriting: 3605e96a66cSDavid du Colombier p = blockRollback(b, buf); 3615e96a66cSDavid du Colombier if(!diskWriteRaw(disk, b->part, b->addr, p)){ 362*f6333ca0SDavid du Colombier fprint(2, "fossil: diskWriteRaw failed: %s: %V: date=%s part=%s addr=%ud: %r\n", 363*f6333ca0SDavid du Colombier disk2file(disk), b->score, ctime(times(0)), partname[b->part], b->addr); 3645e96a66cSDavid du Colombier break; 3655e96a66cSDavid du Colombier } 3665e96a66cSDavid du Colombier if(p != buf) 3675e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 3685e96a66cSDavid du Colombier else 3695e96a66cSDavid du Colombier blockSetIOState(b, BioDirty); 3705e96a66cSDavid du Colombier break; 3715e96a66cSDavid du Colombier } 3725e96a66cSDavid du Colombier 3735e96a66cSDavid du Colombier blockPut(b); /* remove extra reference, unlock */ 3745e96a66cSDavid du Colombier vtLock(disk->lk); 3755e96a66cSDavid du Colombier disk->nqueue--; 3765e96a66cSDavid du Colombier if(disk->nqueue == QueueSize-1) 3775e96a66cSDavid du Colombier vtWakeup(disk->flow); 3785e96a66cSDavid du Colombier if(disk->nqueue == 0) 3795e96a66cSDavid du Colombier vtWakeup(disk->flush); 3805e96a66cSDavid du Colombier nio++; 3815e96a66cSDavid du Colombier } 3825e96a66cSDavid du Colombier Done: 383dc5a79c1SDavid du Colombier //fprint(2, "diskThread done\n"); 3845e96a66cSDavid du Colombier disk->ref--; 3855e96a66cSDavid du Colombier vtWakeup(disk->die); 3865e96a66cSDavid du Colombier vtUnlock(disk->lk); 3875e96a66cSDavid du Colombier vtMemFree(buf); 3885e96a66cSDavid du Colombier } 389