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 { 9*c39c2eb3SDavid du Colombier /* 10*c39c2eb3SDavid du Colombier * disable measurement since it gets alignment faults on BG 11*c39c2eb3SDavid du Colombier * and the guts used to be commented out. 12*c39c2eb3SDavid du Colombier */ 13*c39c2eb3SDavid du Colombier Timing = 0, /* flag */ 145e96a66cSDavid du Colombier QueueSize = 100, /* maximum block to queue */ 155e96a66cSDavid du Colombier }; 165e96a66cSDavid du Colombier 175e96a66cSDavid du Colombier struct Disk { 185e96a66cSDavid du Colombier VtLock *lk; 195e96a66cSDavid du Colombier int ref; 205e96a66cSDavid du Colombier 215e96a66cSDavid du Colombier int fd; 225e96a66cSDavid du Colombier Header h; 235e96a66cSDavid du Colombier 245e96a66cSDavid du Colombier VtRendez *flow; 255e96a66cSDavid du Colombier VtRendez *starve; 265e96a66cSDavid du Colombier VtRendez *flush; 275e96a66cSDavid du Colombier VtRendez *die; 285e96a66cSDavid du Colombier 295e96a66cSDavid du Colombier int nqueue; 305e96a66cSDavid du Colombier 315e96a66cSDavid du Colombier Block *cur; /* block to do on current scan */ 325e96a66cSDavid du Colombier Block *next; /* blocks to do next scan */ 335e96a66cSDavid du Colombier }; 345e96a66cSDavid du Colombier 35f6333ca0SDavid du Colombier /* keep in sync with Part* enum in dat.h */ 36f6333ca0SDavid du Colombier static char *partname[] = { 37f6333ca0SDavid du Colombier [PartError] "error", 38f6333ca0SDavid du Colombier [PartSuper] "super", 39f6333ca0SDavid du Colombier [PartLabel] "label", 40f6333ca0SDavid du Colombier [PartData] "data", 41f6333ca0SDavid du Colombier [PartVenti] "venti", 42f6333ca0SDavid du Colombier }; 435e96a66cSDavid du Colombier 445e96a66cSDavid du Colombier Disk * 455e96a66cSDavid du Colombier diskAlloc(int fd) 465e96a66cSDavid du Colombier { 475e96a66cSDavid du Colombier u8int buf[HeaderSize]; 485e96a66cSDavid du Colombier Header h; 495e96a66cSDavid du Colombier Disk *disk; 505e96a66cSDavid du Colombier 515e96a66cSDavid du Colombier if(pread(fd, buf, HeaderSize, HeaderOffset) < HeaderSize){ 52dc5a79c1SDavid du Colombier vtSetError("short read: %r"); 535e96a66cSDavid du Colombier vtOSError(); 545e96a66cSDavid du Colombier return nil; 555e96a66cSDavid du Colombier } 565e96a66cSDavid du Colombier 57dc5a79c1SDavid du Colombier if(!headerUnpack(&h, buf)){ 58dc5a79c1SDavid du Colombier vtSetError("bad disk header"); 595e96a66cSDavid du Colombier return nil; 60dc5a79c1SDavid du Colombier } 615e96a66cSDavid du Colombier disk = vtMemAllocZ(sizeof(Disk)); 625e96a66cSDavid du Colombier disk->lk = vtLockAlloc(); 635e96a66cSDavid du Colombier disk->starve = vtRendezAlloc(disk->lk); 645e96a66cSDavid du Colombier disk->flow = vtRendezAlloc(disk->lk); 655e96a66cSDavid du Colombier disk->flush = vtRendezAlloc(disk->lk); 665e96a66cSDavid du Colombier disk->fd = fd; 675e96a66cSDavid du Colombier disk->h = h; 685e96a66cSDavid du Colombier 695e96a66cSDavid du Colombier disk->ref = 2; 705e96a66cSDavid du Colombier vtThread(diskThread, disk); 715e96a66cSDavid du Colombier 725e96a66cSDavid du Colombier return disk; 735e96a66cSDavid du Colombier } 745e96a66cSDavid du Colombier 755e96a66cSDavid du Colombier void 765e96a66cSDavid du Colombier diskFree(Disk *disk) 775e96a66cSDavid du Colombier { 785e96a66cSDavid du Colombier diskFlush(disk); 795e96a66cSDavid du Colombier 805e96a66cSDavid du Colombier /* kill slave */ 815e96a66cSDavid du Colombier vtLock(disk->lk); 825e96a66cSDavid du Colombier disk->die = vtRendezAlloc(disk->lk); 835e96a66cSDavid du Colombier vtWakeup(disk->starve); 845e96a66cSDavid du Colombier while(disk->ref > 1) 855e96a66cSDavid du Colombier vtSleep(disk->die); 865e96a66cSDavid du Colombier vtUnlock(disk->lk); 875e96a66cSDavid du Colombier vtRendezFree(disk->flow); 885e96a66cSDavid du Colombier vtRendezFree(disk->starve); 895e96a66cSDavid du Colombier vtRendezFree(disk->die); 905e96a66cSDavid du Colombier vtLockFree(disk->lk); 915e96a66cSDavid du Colombier close(disk->fd); 925e96a66cSDavid du Colombier vtMemFree(disk); 935e96a66cSDavid du Colombier } 945e96a66cSDavid du Colombier 955e96a66cSDavid du Colombier static u32int 965e96a66cSDavid du Colombier partStart(Disk *disk, int part) 975e96a66cSDavid du Colombier { 985e96a66cSDavid du Colombier switch(part){ 995e96a66cSDavid du Colombier default: 1005e96a66cSDavid du Colombier assert(0); 1015e96a66cSDavid du Colombier case PartSuper: 1025e96a66cSDavid du Colombier return disk->h.super; 1035e96a66cSDavid du Colombier case PartLabel: 1045e96a66cSDavid du Colombier return disk->h.label; 1055e96a66cSDavid du Colombier case PartData: 1065e96a66cSDavid du Colombier return disk->h.data; 1075e96a66cSDavid du Colombier } 1085e96a66cSDavid du Colombier } 1095e96a66cSDavid du Colombier 1105e96a66cSDavid du Colombier 1115e96a66cSDavid du Colombier static u32int 1125e96a66cSDavid du Colombier partEnd(Disk *disk, int part) 1135e96a66cSDavid du Colombier { 1145e96a66cSDavid du Colombier switch(part){ 1155e96a66cSDavid du Colombier default: 1165e96a66cSDavid du Colombier assert(0); 1175e96a66cSDavid du Colombier case PartSuper: 1185e96a66cSDavid du Colombier return disk->h.super+1; 1195e96a66cSDavid du Colombier case PartLabel: 1205e96a66cSDavid du Colombier return disk->h.data; 1215e96a66cSDavid du Colombier case PartData: 1225e96a66cSDavid du Colombier return disk->h.end; 1235e96a66cSDavid du Colombier } 1245e96a66cSDavid du Colombier } 1255e96a66cSDavid du Colombier 1265e96a66cSDavid du Colombier int 1275e96a66cSDavid du Colombier diskReadRaw(Disk *disk, int part, u32int addr, uchar *buf) 1285e96a66cSDavid du Colombier { 1295e96a66cSDavid du Colombier ulong start, end; 1305e96a66cSDavid du Colombier u64int offset; 1315e96a66cSDavid du Colombier int n, nn; 1325e96a66cSDavid du Colombier 1335e96a66cSDavid du Colombier start = partStart(disk, part); 1345e96a66cSDavid du Colombier end = partEnd(disk, part); 1355e96a66cSDavid du Colombier 1365e96a66cSDavid du Colombier if(addr >= end-start){ 1375e96a66cSDavid du Colombier vtSetError(EBadAddr); 1385e96a66cSDavid du Colombier return 0; 1395e96a66cSDavid du Colombier } 1405e96a66cSDavid du Colombier 1415e96a66cSDavid du Colombier offset = ((u64int)(addr + start))*disk->h.blockSize; 1425e96a66cSDavid du Colombier n = disk->h.blockSize; 1435e96a66cSDavid du Colombier while(n > 0){ 1445e96a66cSDavid du Colombier nn = pread(disk->fd, buf, n, offset); 1455e96a66cSDavid du Colombier if(nn < 0){ 1465e96a66cSDavid du Colombier vtOSError(); 1475e96a66cSDavid du Colombier return 0; 1485e96a66cSDavid du Colombier } 1495e96a66cSDavid du Colombier if(nn == 0){ 150e569ccb5SDavid du Colombier vtSetError("eof reading disk"); 1515e96a66cSDavid du Colombier return 0; 1525e96a66cSDavid du Colombier } 1535e96a66cSDavid du Colombier n -= nn; 1545e96a66cSDavid du Colombier offset += nn; 1555e96a66cSDavid du Colombier buf += nn; 1565e96a66cSDavid du Colombier } 1575e96a66cSDavid du Colombier return 1; 1585e96a66cSDavid du Colombier } 1595e96a66cSDavid du Colombier 1605e96a66cSDavid du Colombier int 1615e96a66cSDavid du Colombier diskWriteRaw(Disk *disk, int part, u32int addr, uchar *buf) 1625e96a66cSDavid du Colombier { 1635e96a66cSDavid du Colombier ulong start, end; 1645e96a66cSDavid du Colombier u64int offset; 1655e96a66cSDavid du Colombier int n; 1665e96a66cSDavid du Colombier 1675e96a66cSDavid du Colombier start = partStart(disk, part); 1685e96a66cSDavid du Colombier end = partEnd(disk, part); 1695e96a66cSDavid du Colombier 1705e96a66cSDavid du Colombier if(addr >= end - start){ 1715e96a66cSDavid du Colombier vtSetError(EBadAddr); 1725e96a66cSDavid du Colombier return 0; 1735e96a66cSDavid du Colombier } 1745e96a66cSDavid du Colombier 1755e96a66cSDavid du Colombier offset = ((u64int)(addr + start))*disk->h.blockSize; 1766de6ce84SDavid du Colombier n = pwrite(disk->fd, buf, disk->h.blockSize, offset); 1776de6ce84SDavid du Colombier if(n < 0){ 1785e96a66cSDavid du Colombier vtOSError(); 1795e96a66cSDavid du Colombier return 0; 1805e96a66cSDavid du Colombier } 1816de6ce84SDavid du Colombier if(n < disk->h.blockSize) { 1826de6ce84SDavid du Colombier vtSetError("short write"); 1836de6ce84SDavid du Colombier return 0; 1846de6ce84SDavid du Colombier } 1855e96a66cSDavid du Colombier 1865e96a66cSDavid du Colombier return 1; 1875e96a66cSDavid du Colombier } 1885e96a66cSDavid du Colombier 1895e96a66cSDavid du Colombier static void 1905e96a66cSDavid du Colombier diskQueue(Disk *disk, Block *b) 1915e96a66cSDavid du Colombier { 1925e96a66cSDavid du Colombier Block **bp, *bb; 1935e96a66cSDavid du Colombier 1945e96a66cSDavid du Colombier vtLock(disk->lk); 1955e96a66cSDavid du Colombier while(disk->nqueue >= QueueSize) 1965e96a66cSDavid du Colombier vtSleep(disk->flow); 1975e96a66cSDavid du Colombier if(disk->cur == nil || b->addr > disk->cur->addr) 1985e96a66cSDavid du Colombier bp = &disk->cur; 1995e96a66cSDavid du Colombier else 2005e96a66cSDavid du Colombier bp = &disk->next; 2015e96a66cSDavid du Colombier 2025e96a66cSDavid du Colombier for(bb=*bp; bb; bb=*bp){ 2035e96a66cSDavid du Colombier if(b->addr < bb->addr) 2045e96a66cSDavid du Colombier break; 2055e96a66cSDavid du Colombier bp = &bb->ionext; 2065e96a66cSDavid du Colombier } 2075e96a66cSDavid du Colombier b->ionext = bb; 2085e96a66cSDavid du Colombier *bp = b; 2095e96a66cSDavid du Colombier if(disk->nqueue == 0) 2105e96a66cSDavid du Colombier vtWakeup(disk->starve); 2115e96a66cSDavid du Colombier disk->nqueue++; 2125e96a66cSDavid du Colombier vtUnlock(disk->lk); 2135e96a66cSDavid du Colombier } 2145e96a66cSDavid du Colombier 2155e96a66cSDavid du Colombier 2165e96a66cSDavid du Colombier void 2175e96a66cSDavid du Colombier diskRead(Disk *disk, Block *b) 2185e96a66cSDavid du Colombier { 2195e96a66cSDavid du Colombier assert(b->iostate == BioEmpty || b->iostate == BioLabel); 2205e96a66cSDavid du Colombier blockSetIOState(b, BioReading); 2215e96a66cSDavid du Colombier diskQueue(disk, b); 2225e96a66cSDavid du Colombier } 2235e96a66cSDavid du Colombier 2245e96a66cSDavid du Colombier void 2255e96a66cSDavid du Colombier diskWrite(Disk *disk, Block *b) 2265e96a66cSDavid du Colombier { 227867bfcc6SDavid du Colombier assert(b->nlock == 1); 2285e96a66cSDavid du Colombier assert(b->iostate == BioDirty); 2295e96a66cSDavid du Colombier blockSetIOState(b, BioWriting); 2305e96a66cSDavid du Colombier diskQueue(disk, b); 2315e96a66cSDavid du Colombier } 2325e96a66cSDavid du Colombier 233867bfcc6SDavid du Colombier void 234867bfcc6SDavid du Colombier diskWriteAndWait(Disk *disk, Block *b) 235867bfcc6SDavid du Colombier { 236867bfcc6SDavid du Colombier int nlock; 237867bfcc6SDavid du Colombier 238867bfcc6SDavid du Colombier /* 239867bfcc6SDavid du Colombier * If b->nlock > 1, the block is aliased within 240867bfcc6SDavid du Colombier * a single thread. That thread is us. 241867bfcc6SDavid du Colombier * DiskWrite does some funny stuff with VtLock 242867bfcc6SDavid du Colombier * and blockPut that basically assumes b->nlock==1. 243867bfcc6SDavid du Colombier * We humor diskWrite by temporarily setting 244867bfcc6SDavid du Colombier * nlock to 1. This needs to be revisited. 245867bfcc6SDavid du Colombier */ 246867bfcc6SDavid du Colombier nlock = b->nlock; 247867bfcc6SDavid du Colombier if(nlock > 1) 248867bfcc6SDavid du Colombier b->nlock = 1; 249867bfcc6SDavid du Colombier diskWrite(disk, b); 250867bfcc6SDavid du Colombier while(b->iostate != BioClean) 251867bfcc6SDavid du Colombier vtSleep(b->ioready); 252867bfcc6SDavid du Colombier b->nlock = nlock; 253867bfcc6SDavid du Colombier } 254867bfcc6SDavid du Colombier 2555e96a66cSDavid du Colombier int 2565e96a66cSDavid du Colombier diskBlockSize(Disk *disk) 2575e96a66cSDavid du Colombier { 2585e96a66cSDavid du Colombier return disk->h.blockSize; /* immuttable */ 2595e96a66cSDavid du Colombier } 2605e96a66cSDavid du Colombier 2615e96a66cSDavid du Colombier int 2625e96a66cSDavid du Colombier diskFlush(Disk *disk) 2635e96a66cSDavid du Colombier { 2645e96a66cSDavid du Colombier Dir dir; 2655e96a66cSDavid du Colombier 2665e96a66cSDavid du Colombier vtLock(disk->lk); 2675e96a66cSDavid du Colombier while(disk->nqueue > 0) 2685e96a66cSDavid du Colombier vtSleep(disk->flush); 2695e96a66cSDavid du Colombier vtUnlock(disk->lk); 2705e96a66cSDavid du Colombier 2715e96a66cSDavid du Colombier /* there really should be a cleaner interface to flush an fd */ 2725e96a66cSDavid du Colombier nulldir(&dir); 2735e96a66cSDavid du Colombier if(dirfwstat(disk->fd, &dir) < 0){ 2745e96a66cSDavid du Colombier vtOSError(); 2755e96a66cSDavid du Colombier return 0; 2765e96a66cSDavid du Colombier } 2775e96a66cSDavid du Colombier return 1; 2785e96a66cSDavid du Colombier } 2795e96a66cSDavid du Colombier 2805e96a66cSDavid du Colombier u32int 2815e96a66cSDavid du Colombier diskSize(Disk *disk, int part) 2825e96a66cSDavid du Colombier { 2835e96a66cSDavid du Colombier return partEnd(disk, part) - partStart(disk, part); 2845e96a66cSDavid du Colombier } 2855e96a66cSDavid du Colombier 28674f16c81SDavid du Colombier static uintptr 287fe853e23SDavid du Colombier mypc(int x) 288fe853e23SDavid du Colombier { 289fe853e23SDavid du Colombier return getcallerpc(&x); 290fe853e23SDavid du Colombier } 291fe853e23SDavid du Colombier 292f6333ca0SDavid du Colombier static char * 293f6333ca0SDavid du Colombier disk2file(Disk *disk) 294f6333ca0SDavid du Colombier { 295f6333ca0SDavid du Colombier static char buf[256]; 296f6333ca0SDavid du Colombier 297f6333ca0SDavid du Colombier if (fd2path(disk->fd, buf, sizeof buf) < 0) 298f6333ca0SDavid du Colombier strncpy(buf, "GOK", sizeof buf); 299f6333ca0SDavid du Colombier return buf; 300f6333ca0SDavid du Colombier } 301f6333ca0SDavid du Colombier 3025e96a66cSDavid du Colombier static void 3035e96a66cSDavid du Colombier diskThread(void *a) 3045e96a66cSDavid du Colombier { 3055e96a66cSDavid du Colombier Disk *disk = a; 3065e96a66cSDavid du Colombier Block *b; 3075e96a66cSDavid du Colombier uchar *buf, *p; 3085e96a66cSDavid du Colombier double t; 3095e96a66cSDavid du Colombier int nio; 3105e96a66cSDavid du Colombier 3115e96a66cSDavid du Colombier vtThreadSetName("disk"); 3125e96a66cSDavid du Colombier 313dc5a79c1SDavid du Colombier //fprint(2, "diskThread %d\n", getpid()); 3145e96a66cSDavid du Colombier 3155e96a66cSDavid du Colombier buf = vtMemAlloc(disk->h.blockSize); 3165e96a66cSDavid du Colombier 3175e96a66cSDavid du Colombier vtLock(disk->lk); 318*c39c2eb3SDavid du Colombier if (Timing) { 3195e96a66cSDavid du Colombier nio = 0; 3205e96a66cSDavid du Colombier t = -nsec(); 321*c39c2eb3SDavid du Colombier } 3225e96a66cSDavid du Colombier for(;;){ 3235e96a66cSDavid du Colombier while(disk->nqueue == 0){ 324*c39c2eb3SDavid du Colombier if (Timing) { 3255e96a66cSDavid du Colombier t += nsec(); 326*c39c2eb3SDavid du Colombier if(nio >= 10000){ 327*c39c2eb3SDavid du Colombier fprint(2, "disk: io=%d at %.3fms\n", 328*c39c2eb3SDavid du Colombier nio, t*1e-6/nio); 329*c39c2eb3SDavid du Colombier nio = 0; 330*c39c2eb3SDavid du Colombier t = 0; 331*c39c2eb3SDavid du Colombier } 332*c39c2eb3SDavid du Colombier } 3335e96a66cSDavid du Colombier if(disk->die != nil) 3345e96a66cSDavid du Colombier goto Done; 3355e96a66cSDavid du Colombier vtSleep(disk->starve); 336*c39c2eb3SDavid du Colombier if (Timing) 3375e96a66cSDavid du Colombier t -= nsec(); 3385e96a66cSDavid du Colombier } 3395e96a66cSDavid du Colombier assert(disk->cur != nil || disk->next != nil); 3405e96a66cSDavid du Colombier 3415e96a66cSDavid du Colombier if(disk->cur == nil){ 3425e96a66cSDavid du Colombier disk->cur = disk->next; 3435e96a66cSDavid du Colombier disk->next = nil; 3445e96a66cSDavid du Colombier } 3455e96a66cSDavid du Colombier b = disk->cur; 3465e96a66cSDavid du Colombier disk->cur = b->ionext; 3475e96a66cSDavid du Colombier vtUnlock(disk->lk); 3485e96a66cSDavid du Colombier 3495e96a66cSDavid du Colombier /* 3505e96a66cSDavid du Colombier * no one should hold onto blocking in the 3515e96a66cSDavid du Colombier * reading or writing state, so this lock should 3525e96a66cSDavid du Colombier * not cause deadlock. 3535e96a66cSDavid du Colombier */ 354f6333ca0SDavid du Colombier if(0)fprint(2, "fossil: diskThread: %d:%d %x\n", getpid(), b->part, b->addr); 3555e96a66cSDavid du Colombier bwatchLock(b); 3565e96a66cSDavid du Colombier vtLock(b->lk); 357fe853e23SDavid du Colombier b->pc = mypc(0); 3585e96a66cSDavid du Colombier assert(b->nlock == 1); 3595e96a66cSDavid du Colombier switch(b->iostate){ 3605e96a66cSDavid du Colombier default: 3615e96a66cSDavid du Colombier abort(); 3625e96a66cSDavid du Colombier case BioReading: 3635e96a66cSDavid du Colombier if(!diskReadRaw(disk, b->part, b->addr, b->data)){ 364f366f900SDavid du Colombier fprint(2, "fossil: diskReadRaw failed: %s: " 365f366f900SDavid du Colombier "score %V: part=%s block %ud: %r\n", 366f366f900SDavid du Colombier disk2file(disk), b->score, 367f366f900SDavid du Colombier partname[b->part], b->addr); 3685e96a66cSDavid du Colombier blockSetIOState(b, BioReadError); 3695e96a66cSDavid du Colombier }else 3705e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 3715e96a66cSDavid du Colombier break; 3725e96a66cSDavid du Colombier case BioWriting: 3735e96a66cSDavid du Colombier p = blockRollback(b, buf); 374f366f900SDavid du Colombier /* NB: ctime result ends with a newline */ 3755e96a66cSDavid du Colombier if(!diskWriteRaw(disk, b->part, b->addr, p)){ 376f366f900SDavid du Colombier fprint(2, "fossil: diskWriteRaw failed: %s: " 377f366f900SDavid du Colombier "score %V: date %s part=%s block %ud: %r\n", 378f366f900SDavid du Colombier disk2file(disk), b->score, 379f366f900SDavid du Colombier ctime(time(0)), 380f366f900SDavid du Colombier partname[b->part], b->addr); 3815e96a66cSDavid du Colombier break; 3825e96a66cSDavid du Colombier } 3835e96a66cSDavid du Colombier if(p != buf) 3845e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 3855e96a66cSDavid du Colombier else 3865e96a66cSDavid du Colombier blockSetIOState(b, BioDirty); 3875e96a66cSDavid du Colombier break; 3885e96a66cSDavid du Colombier } 3895e96a66cSDavid du Colombier 3905e96a66cSDavid du Colombier blockPut(b); /* remove extra reference, unlock */ 3915e96a66cSDavid du Colombier vtLock(disk->lk); 3925e96a66cSDavid du Colombier disk->nqueue--; 3935e96a66cSDavid du Colombier if(disk->nqueue == QueueSize-1) 3945e96a66cSDavid du Colombier vtWakeup(disk->flow); 3955e96a66cSDavid du Colombier if(disk->nqueue == 0) 3965e96a66cSDavid du Colombier vtWakeup(disk->flush); 397*c39c2eb3SDavid du Colombier if(Timing) 3985e96a66cSDavid du Colombier nio++; 3995e96a66cSDavid du Colombier } 4005e96a66cSDavid du Colombier Done: 401dc5a79c1SDavid du Colombier //fprint(2, "diskThread done\n"); 4025e96a66cSDavid du Colombier disk->ref--; 4035e96a66cSDavid du Colombier vtWakeup(disk->die); 4045e96a66cSDavid du Colombier vtUnlock(disk->lk); 4055e96a66cSDavid du Colombier vtMemFree(buf); 4065e96a66cSDavid du Colombier } 407