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 305e96a66cSDavid du Colombier 315e96a66cSDavid du Colombier Disk * 325e96a66cSDavid du Colombier diskAlloc(int fd) 335e96a66cSDavid du Colombier { 345e96a66cSDavid du Colombier u8int buf[HeaderSize]; 355e96a66cSDavid du Colombier Header h; 365e96a66cSDavid du Colombier Disk *disk; 375e96a66cSDavid du Colombier 385e96a66cSDavid du Colombier if(pread(fd, buf, HeaderSize, HeaderOffset) < HeaderSize){ 39dc5a79c1SDavid du Colombier vtSetError("short read: %r"); 405e96a66cSDavid du Colombier vtOSError(); 415e96a66cSDavid du Colombier return nil; 425e96a66cSDavid du Colombier } 435e96a66cSDavid du Colombier 44dc5a79c1SDavid du Colombier if(!headerUnpack(&h, buf)){ 45dc5a79c1SDavid du Colombier vtSetError("bad disk header"); 465e96a66cSDavid du Colombier return nil; 47dc5a79c1SDavid du Colombier } 485e96a66cSDavid du Colombier disk = vtMemAllocZ(sizeof(Disk)); 495e96a66cSDavid du Colombier disk->lk = vtLockAlloc(); 505e96a66cSDavid du Colombier disk->starve = vtRendezAlloc(disk->lk); 515e96a66cSDavid du Colombier disk->flow = vtRendezAlloc(disk->lk); 525e96a66cSDavid du Colombier disk->flush = vtRendezAlloc(disk->lk); 535e96a66cSDavid du Colombier disk->fd = fd; 545e96a66cSDavid du Colombier disk->h = h; 555e96a66cSDavid du Colombier 565e96a66cSDavid du Colombier disk->ref = 2; 575e96a66cSDavid du Colombier vtThread(diskThread, disk); 585e96a66cSDavid du Colombier 595e96a66cSDavid du Colombier return disk; 605e96a66cSDavid du Colombier } 615e96a66cSDavid du Colombier 625e96a66cSDavid du Colombier void 635e96a66cSDavid du Colombier diskFree(Disk *disk) 645e96a66cSDavid du Colombier { 655e96a66cSDavid du Colombier diskFlush(disk); 665e96a66cSDavid du Colombier 675e96a66cSDavid du Colombier /* kill slave */ 685e96a66cSDavid du Colombier vtLock(disk->lk); 695e96a66cSDavid du Colombier disk->die = vtRendezAlloc(disk->lk); 705e96a66cSDavid du Colombier vtWakeup(disk->starve); 715e96a66cSDavid du Colombier while(disk->ref > 1) 725e96a66cSDavid du Colombier vtSleep(disk->die); 735e96a66cSDavid du Colombier vtUnlock(disk->lk); 745e96a66cSDavid du Colombier vtRendezFree(disk->flow); 755e96a66cSDavid du Colombier vtRendezFree(disk->starve); 765e96a66cSDavid du Colombier vtRendezFree(disk->die); 775e96a66cSDavid du Colombier vtLockFree(disk->lk); 785e96a66cSDavid du Colombier close(disk->fd); 795e96a66cSDavid du Colombier vtMemFree(disk); 805e96a66cSDavid du Colombier } 815e96a66cSDavid du Colombier 825e96a66cSDavid du Colombier static u32int 835e96a66cSDavid du Colombier partStart(Disk *disk, int part) 845e96a66cSDavid du Colombier { 855e96a66cSDavid du Colombier switch(part){ 865e96a66cSDavid du Colombier default: 875e96a66cSDavid du Colombier assert(0); 885e96a66cSDavid du Colombier case PartSuper: 895e96a66cSDavid du Colombier return disk->h.super; 905e96a66cSDavid du Colombier case PartLabel: 915e96a66cSDavid du Colombier return disk->h.label; 925e96a66cSDavid du Colombier case PartData: 935e96a66cSDavid du Colombier return disk->h.data; 945e96a66cSDavid du Colombier } 955e96a66cSDavid du Colombier } 965e96a66cSDavid du Colombier 975e96a66cSDavid du Colombier 985e96a66cSDavid du Colombier static u32int 995e96a66cSDavid du Colombier partEnd(Disk *disk, int part) 1005e96a66cSDavid du Colombier { 1015e96a66cSDavid du Colombier switch(part){ 1025e96a66cSDavid du Colombier default: 1035e96a66cSDavid du Colombier assert(0); 1045e96a66cSDavid du Colombier case PartSuper: 1055e96a66cSDavid du Colombier return disk->h.super+1; 1065e96a66cSDavid du Colombier case PartLabel: 1075e96a66cSDavid du Colombier return disk->h.data; 1085e96a66cSDavid du Colombier case PartData: 1095e96a66cSDavid du Colombier return disk->h.end; 1105e96a66cSDavid du Colombier } 1115e96a66cSDavid du Colombier } 1125e96a66cSDavid du Colombier 1135e96a66cSDavid du Colombier int 1145e96a66cSDavid du Colombier diskReadRaw(Disk *disk, int part, u32int addr, uchar *buf) 1155e96a66cSDavid du Colombier { 1165e96a66cSDavid du Colombier ulong start, end; 1175e96a66cSDavid du Colombier u64int offset; 1185e96a66cSDavid du Colombier int n, nn; 1195e96a66cSDavid du Colombier 1205e96a66cSDavid du Colombier start = partStart(disk, part); 1215e96a66cSDavid du Colombier end = partEnd(disk, part); 1225e96a66cSDavid du Colombier 1235e96a66cSDavid du Colombier if(addr >= end-start){ 1245e96a66cSDavid du Colombier vtSetError(EBadAddr); 1255e96a66cSDavid du Colombier return 0; 1265e96a66cSDavid du Colombier } 1275e96a66cSDavid du Colombier 1285e96a66cSDavid du Colombier offset = ((u64int)(addr + start))*disk->h.blockSize; 1295e96a66cSDavid du Colombier n = disk->h.blockSize; 1305e96a66cSDavid du Colombier while(n > 0){ 1315e96a66cSDavid du Colombier nn = pread(disk->fd, buf, n, offset); 1325e96a66cSDavid du Colombier if(nn < 0){ 1335e96a66cSDavid du Colombier vtOSError(); 1345e96a66cSDavid du Colombier return 0; 1355e96a66cSDavid du Colombier } 1365e96a66cSDavid du Colombier if(nn == 0){ 1375e96a66cSDavid du Colombier vtSetError(EIO); 1385e96a66cSDavid du Colombier return 0; 1395e96a66cSDavid du Colombier } 1405e96a66cSDavid du Colombier n -= nn; 1415e96a66cSDavid du Colombier offset += nn; 1425e96a66cSDavid du Colombier buf += nn; 1435e96a66cSDavid du Colombier } 1445e96a66cSDavid du Colombier return 1; 1455e96a66cSDavid du Colombier } 1465e96a66cSDavid du Colombier 1475e96a66cSDavid du Colombier int 1485e96a66cSDavid du Colombier diskWriteRaw(Disk *disk, int part, u32int addr, uchar *buf) 1495e96a66cSDavid du Colombier { 1505e96a66cSDavid du Colombier ulong start, end; 1515e96a66cSDavid du Colombier u64int offset; 1525e96a66cSDavid du Colombier int n; 1535e96a66cSDavid du Colombier 1545e96a66cSDavid du Colombier start = partStart(disk, part); 1555e96a66cSDavid du Colombier end = partEnd(disk, part); 1565e96a66cSDavid du Colombier 1575e96a66cSDavid du Colombier if(addr >= end-start){ 1585e96a66cSDavid du Colombier vtSetError(EBadAddr); 1595e96a66cSDavid du Colombier return 0; 1605e96a66cSDavid du Colombier } 1615e96a66cSDavid du Colombier 1625e96a66cSDavid du Colombier offset = ((u64int)(addr + start))*disk->h.blockSize; 1636de6ce84SDavid du Colombier n = pwrite(disk->fd, buf, disk->h.blockSize, offset); 1646de6ce84SDavid du Colombier if(n < 0){ 1655e96a66cSDavid du Colombier vtOSError(); 1665e96a66cSDavid du Colombier return 0; 1675e96a66cSDavid du Colombier } 1686de6ce84SDavid du Colombier if(n < disk->h.blockSize) { 1696de6ce84SDavid du Colombier vtSetError("short write"); 1706de6ce84SDavid du Colombier return 0; 1716de6ce84SDavid du Colombier } 1725e96a66cSDavid du Colombier 1735e96a66cSDavid du Colombier return 1; 1745e96a66cSDavid du Colombier } 1755e96a66cSDavid du Colombier 1765e96a66cSDavid du Colombier static void 1775e96a66cSDavid du Colombier diskQueue(Disk *disk, Block *b) 1785e96a66cSDavid du Colombier { 1795e96a66cSDavid du Colombier Block **bp, *bb; 1805e96a66cSDavid du Colombier 1815e96a66cSDavid du Colombier vtLock(disk->lk); 1825e96a66cSDavid du Colombier while(disk->nqueue >= QueueSize) 1835e96a66cSDavid du Colombier vtSleep(disk->flow); 1845e96a66cSDavid du Colombier if(disk->cur == nil || b->addr > disk->cur->addr) 1855e96a66cSDavid du Colombier bp = &disk->cur; 1865e96a66cSDavid du Colombier else 1875e96a66cSDavid du Colombier bp = &disk->next; 1885e96a66cSDavid du Colombier 1895e96a66cSDavid du Colombier for(bb=*bp; bb; bb=*bp){ 1905e96a66cSDavid du Colombier if(b->addr < bb->addr) 1915e96a66cSDavid du Colombier break; 1925e96a66cSDavid du Colombier bp = &bb->ionext; 1935e96a66cSDavid du Colombier } 1945e96a66cSDavid du Colombier b->ionext = bb; 1955e96a66cSDavid du Colombier *bp = b; 1965e96a66cSDavid du Colombier if(disk->nqueue == 0) 1975e96a66cSDavid du Colombier vtWakeup(disk->starve); 1985e96a66cSDavid du Colombier disk->nqueue++; 1995e96a66cSDavid du Colombier vtUnlock(disk->lk); 2005e96a66cSDavid du Colombier } 2015e96a66cSDavid du Colombier 2025e96a66cSDavid du Colombier 2035e96a66cSDavid du Colombier void 2045e96a66cSDavid du Colombier diskRead(Disk *disk, Block *b) 2055e96a66cSDavid du Colombier { 2065e96a66cSDavid du Colombier assert(b->iostate == BioEmpty || b->iostate == BioLabel); 2075e96a66cSDavid du Colombier blockSetIOState(b, BioReading); 2085e96a66cSDavid du Colombier diskQueue(disk, b); 2095e96a66cSDavid du Colombier } 2105e96a66cSDavid du Colombier 2115e96a66cSDavid du Colombier void 2125e96a66cSDavid du Colombier diskWrite(Disk *disk, Block *b) 2135e96a66cSDavid du Colombier { 214867bfcc6SDavid du Colombier assert(b->nlock == 1); 2155e96a66cSDavid du Colombier assert(b->iostate == BioDirty); 2165e96a66cSDavid du Colombier blockSetIOState(b, BioWriting); 2175e96a66cSDavid du Colombier diskQueue(disk, b); 2185e96a66cSDavid du Colombier } 2195e96a66cSDavid du Colombier 220867bfcc6SDavid du Colombier void 221867bfcc6SDavid du Colombier diskWriteAndWait(Disk *disk, Block *b) 222867bfcc6SDavid du Colombier { 223867bfcc6SDavid du Colombier int nlock; 224867bfcc6SDavid du Colombier 225867bfcc6SDavid du Colombier /* 226867bfcc6SDavid du Colombier * If b->nlock > 1, the block is aliased within 227867bfcc6SDavid du Colombier * a single thread. That thread is us. 228867bfcc6SDavid du Colombier * DiskWrite does some funny stuff with VtLock 229867bfcc6SDavid du Colombier * and blockPut that basically assumes b->nlock==1. 230867bfcc6SDavid du Colombier * We humor diskWrite by temporarily setting 231867bfcc6SDavid du Colombier * nlock to 1. This needs to be revisited. 232867bfcc6SDavid du Colombier */ 233867bfcc6SDavid du Colombier nlock = b->nlock; 234867bfcc6SDavid du Colombier if(nlock > 1) 235867bfcc6SDavid du Colombier b->nlock = 1; 236867bfcc6SDavid du Colombier diskWrite(disk, b); 237867bfcc6SDavid du Colombier while(b->iostate != BioClean) 238867bfcc6SDavid du Colombier vtSleep(b->ioready); 239867bfcc6SDavid du Colombier b->nlock = nlock; 240867bfcc6SDavid du Colombier } 241867bfcc6SDavid du Colombier 2425e96a66cSDavid du Colombier int 2435e96a66cSDavid du Colombier diskBlockSize(Disk *disk) 2445e96a66cSDavid du Colombier { 2455e96a66cSDavid du Colombier return disk->h.blockSize; /* immuttable */ 2465e96a66cSDavid du Colombier } 2475e96a66cSDavid du Colombier 2485e96a66cSDavid du Colombier int 2495e96a66cSDavid du Colombier diskFlush(Disk *disk) 2505e96a66cSDavid du Colombier { 2515e96a66cSDavid du Colombier Dir dir; 2525e96a66cSDavid du Colombier 2535e96a66cSDavid du Colombier vtLock(disk->lk); 2545e96a66cSDavid du Colombier while(disk->nqueue > 0) 2555e96a66cSDavid du Colombier vtSleep(disk->flush); 2565e96a66cSDavid du Colombier vtUnlock(disk->lk); 2575e96a66cSDavid du Colombier 2585e96a66cSDavid du Colombier /* there really should be a cleaner interface to flush an fd */ 2595e96a66cSDavid du Colombier nulldir(&dir); 2605e96a66cSDavid du Colombier if(dirfwstat(disk->fd, &dir) < 0){ 2615e96a66cSDavid du Colombier vtOSError(); 2625e96a66cSDavid du Colombier return 0; 2635e96a66cSDavid du Colombier } 2645e96a66cSDavid du Colombier return 1; 2655e96a66cSDavid du Colombier } 2665e96a66cSDavid du Colombier 2675e96a66cSDavid du Colombier u32int 2685e96a66cSDavid du Colombier diskSize(Disk *disk, int part) 2695e96a66cSDavid du Colombier { 2705e96a66cSDavid du Colombier return partEnd(disk, part) - partStart(disk, part); 2715e96a66cSDavid du Colombier } 2725e96a66cSDavid du Colombier 273*fe853e23SDavid du Colombier static ulong 274*fe853e23SDavid du Colombier mypc(int x) 275*fe853e23SDavid du Colombier { 276*fe853e23SDavid du Colombier return getcallerpc(&x); 277*fe853e23SDavid du Colombier } 278*fe853e23SDavid du Colombier 2795e96a66cSDavid du Colombier static void 2805e96a66cSDavid du Colombier diskThread(void *a) 2815e96a66cSDavid du Colombier { 2825e96a66cSDavid du Colombier Disk *disk = a; 2835e96a66cSDavid du Colombier Block *b; 2845e96a66cSDavid du Colombier uchar *buf, *p; 2855e96a66cSDavid du Colombier double t; 2865e96a66cSDavid du Colombier int nio; 2875e96a66cSDavid du Colombier 2885e96a66cSDavid du Colombier vtThreadSetName("disk"); 2895e96a66cSDavid du Colombier 290dc5a79c1SDavid du Colombier //fprint(2, "diskThread %d\n", getpid()); 2915e96a66cSDavid du Colombier 2925e96a66cSDavid du Colombier buf = vtMemAlloc(disk->h.blockSize); 2935e96a66cSDavid du Colombier 2945e96a66cSDavid du Colombier vtLock(disk->lk); 2955e96a66cSDavid du Colombier nio = 0; 2965e96a66cSDavid du Colombier t = -nsec(); 2975e96a66cSDavid du Colombier for(;;){ 2985e96a66cSDavid du Colombier while(disk->nqueue == 0){ 2995e96a66cSDavid du Colombier t += nsec(); 30039734e7eSDavid du Colombier //if(nio >= 10000){ 30139734e7eSDavid du Colombier //fprint(2, "disk: io=%d at %.3fms\n", nio, t*1e-6/nio); 30239734e7eSDavid du Colombier //nio = 0; 30339734e7eSDavid du Colombier //t = 0.; 30439734e7eSDavid du Colombier //} 3055e96a66cSDavid du Colombier if(disk->die != nil) 3065e96a66cSDavid du Colombier goto Done; 3075e96a66cSDavid du Colombier vtSleep(disk->starve); 3085e96a66cSDavid du Colombier t -= nsec(); 3095e96a66cSDavid du Colombier } 3105e96a66cSDavid du Colombier assert(disk->cur != nil || disk->next != nil); 3115e96a66cSDavid du Colombier 3125e96a66cSDavid du Colombier if(disk->cur == nil){ 3135e96a66cSDavid du Colombier disk->cur = disk->next; 3145e96a66cSDavid du Colombier disk->next = nil; 3155e96a66cSDavid du Colombier } 3165e96a66cSDavid du Colombier b = disk->cur; 3175e96a66cSDavid du Colombier disk->cur = b->ionext; 3185e96a66cSDavid du Colombier vtUnlock(disk->lk); 3195e96a66cSDavid du Colombier 3205e96a66cSDavid du Colombier /* 3215e96a66cSDavid du Colombier * no one should hold onto blocking in the 3225e96a66cSDavid du Colombier * reading or writing state, so this lock should 3235e96a66cSDavid du Colombier * not cause deadlock. 3245e96a66cSDavid du Colombier */ 3255e96a66cSDavid du Colombier if(0)fprint(2, "diskThread: %d:%d %x\n", getpid(), b->part, b->addr); 3265e96a66cSDavid du Colombier bwatchLock(b); 3275e96a66cSDavid du Colombier vtLock(b->lk); 328*fe853e23SDavid du Colombier b->pc = mypc(0); 3295e96a66cSDavid du Colombier assert(b->nlock == 1); 3305e96a66cSDavid du Colombier switch(b->iostate){ 3315e96a66cSDavid du Colombier default: 3325e96a66cSDavid du Colombier abort(); 3335e96a66cSDavid du Colombier case BioReading: 3345e96a66cSDavid du Colombier if(!diskReadRaw(disk, b->part, b->addr, b->data)){ 3356de6ce84SDavid du Colombier fprint(2, "diskReadRaw failed: part=%d addr=%ux: %r\n", b->part, b->addr); 3365e96a66cSDavid du Colombier blockSetIOState(b, BioReadError); 3375e96a66cSDavid du Colombier }else 3385e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 3395e96a66cSDavid du Colombier break; 3405e96a66cSDavid du Colombier case BioWriting: 3415e96a66cSDavid du Colombier p = blockRollback(b, buf); 3425e96a66cSDavid du Colombier if(!diskWriteRaw(disk, b->part, b->addr, p)){ 3436de6ce84SDavid du Colombier fprint(2, "diskWriteRaw failed: date=%s part=%d addr=%ux: %r\n", ctime(times(0)), b->part, b->addr); 3445e96a66cSDavid du Colombier break; 3455e96a66cSDavid du Colombier } 3465e96a66cSDavid du Colombier if(p != buf) 3475e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 3485e96a66cSDavid du Colombier else 3495e96a66cSDavid du Colombier blockSetIOState(b, BioDirty); 3505e96a66cSDavid du Colombier break; 3515e96a66cSDavid du Colombier } 3525e96a66cSDavid du Colombier 3535e96a66cSDavid du Colombier blockPut(b); /* remove extra reference, unlock */ 3545e96a66cSDavid du Colombier vtLock(disk->lk); 3555e96a66cSDavid du Colombier disk->nqueue--; 3565e96a66cSDavid du Colombier if(disk->nqueue == QueueSize-1) 3575e96a66cSDavid du Colombier vtWakeup(disk->flow); 3585e96a66cSDavid du Colombier if(disk->nqueue == 0) 3595e96a66cSDavid du Colombier vtWakeup(disk->flush); 3605e96a66cSDavid du Colombier nio++; 3615e96a66cSDavid du Colombier } 3625e96a66cSDavid du Colombier Done: 363dc5a79c1SDavid du Colombier //fprint(2, "diskThread done\n"); 3645e96a66cSDavid du Colombier disk->ref--; 3655e96a66cSDavid du Colombier vtWakeup(disk->die); 3665e96a66cSDavid du Colombier vtUnlock(disk->lk); 3675e96a66cSDavid du Colombier vtMemFree(buf); 3685e96a66cSDavid du Colombier } 369