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){ 395e96a66cSDavid du Colombier vtOSError(); 405e96a66cSDavid du Colombier return nil; 415e96a66cSDavid du Colombier } 425e96a66cSDavid du Colombier 435e96a66cSDavid du Colombier if(!headerUnpack(&h, buf)) 445e96a66cSDavid du Colombier return nil; 455e96a66cSDavid du Colombier disk = vtMemAllocZ(sizeof(Disk)); 465e96a66cSDavid du Colombier disk->lk = vtLockAlloc(); 475e96a66cSDavid du Colombier disk->starve = vtRendezAlloc(disk->lk); 485e96a66cSDavid du Colombier disk->flow = vtRendezAlloc(disk->lk); 495e96a66cSDavid du Colombier disk->flush = vtRendezAlloc(disk->lk); 505e96a66cSDavid du Colombier disk->fd = fd; 515e96a66cSDavid du Colombier disk->h = h; 525e96a66cSDavid du Colombier 535e96a66cSDavid du Colombier disk->ref = 2; 545e96a66cSDavid du Colombier vtThread(diskThread, disk); 555e96a66cSDavid du Colombier 565e96a66cSDavid du Colombier return disk; 575e96a66cSDavid du Colombier } 585e96a66cSDavid du Colombier 595e96a66cSDavid du Colombier void 605e96a66cSDavid du Colombier diskFree(Disk *disk) 615e96a66cSDavid du Colombier { 625e96a66cSDavid du Colombier diskFlush(disk); 635e96a66cSDavid du Colombier 645e96a66cSDavid du Colombier /* kill slave */ 655e96a66cSDavid du Colombier vtLock(disk->lk); 665e96a66cSDavid du Colombier disk->die = vtRendezAlloc(disk->lk); 675e96a66cSDavid du Colombier vtWakeup(disk->starve); 685e96a66cSDavid du Colombier while(disk->ref > 1) 695e96a66cSDavid du Colombier vtSleep(disk->die); 705e96a66cSDavid du Colombier vtUnlock(disk->lk); 715e96a66cSDavid du Colombier vtRendezFree(disk->flow); 725e96a66cSDavid du Colombier vtRendezFree(disk->starve); 735e96a66cSDavid du Colombier vtRendezFree(disk->die); 745e96a66cSDavid du Colombier vtLockFree(disk->lk); 755e96a66cSDavid du Colombier close(disk->fd); 765e96a66cSDavid du Colombier vtMemFree(disk); 775e96a66cSDavid du Colombier } 785e96a66cSDavid du Colombier 795e96a66cSDavid du Colombier static u32int 805e96a66cSDavid du Colombier partStart(Disk *disk, int part) 815e96a66cSDavid du Colombier { 825e96a66cSDavid du Colombier switch(part){ 835e96a66cSDavid du Colombier default: 845e96a66cSDavid du Colombier assert(0); 855e96a66cSDavid du Colombier case PartSuper: 865e96a66cSDavid du Colombier return disk->h.super; 875e96a66cSDavid du Colombier case PartLabel: 885e96a66cSDavid du Colombier return disk->h.label; 895e96a66cSDavid du Colombier case PartData: 905e96a66cSDavid du Colombier return disk->h.data; 915e96a66cSDavid du Colombier } 925e96a66cSDavid du Colombier } 935e96a66cSDavid du Colombier 945e96a66cSDavid du Colombier 955e96a66cSDavid du Colombier static u32int 965e96a66cSDavid du Colombier partEnd(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+1; 1035e96a66cSDavid du Colombier case PartLabel: 1045e96a66cSDavid du Colombier return disk->h.data; 1055e96a66cSDavid du Colombier case PartData: 1065e96a66cSDavid du Colombier return disk->h.end; 1075e96a66cSDavid du Colombier } 1085e96a66cSDavid du Colombier } 1095e96a66cSDavid du Colombier 1105e96a66cSDavid du Colombier int 1115e96a66cSDavid du Colombier diskReadRaw(Disk *disk, int part, u32int addr, uchar *buf) 1125e96a66cSDavid du Colombier { 1135e96a66cSDavid du Colombier ulong start, end; 1145e96a66cSDavid du Colombier u64int offset; 1155e96a66cSDavid du Colombier int n, nn; 1165e96a66cSDavid du Colombier 1175e96a66cSDavid du Colombier start = partStart(disk, part); 1185e96a66cSDavid du Colombier end = partEnd(disk, part); 1195e96a66cSDavid du Colombier 1205e96a66cSDavid du Colombier if(addr >= end-start){ 1215e96a66cSDavid du Colombier vtSetError(EBadAddr); 1225e96a66cSDavid du Colombier return 0; 1235e96a66cSDavid du Colombier } 1245e96a66cSDavid du Colombier 1255e96a66cSDavid du Colombier offset = ((u64int)(addr + start))*disk->h.blockSize; 1265e96a66cSDavid du Colombier n = disk->h.blockSize; 1275e96a66cSDavid du Colombier while(n > 0){ 1285e96a66cSDavid du Colombier nn = pread(disk->fd, buf, n, offset); 1295e96a66cSDavid du Colombier if(nn < 0){ 1305e96a66cSDavid du Colombier vtOSError(); 1315e96a66cSDavid du Colombier return 0; 1325e96a66cSDavid du Colombier } 1335e96a66cSDavid du Colombier if(nn == 0){ 1345e96a66cSDavid du Colombier vtSetError(EIO); 1355e96a66cSDavid du Colombier return 0; 1365e96a66cSDavid du Colombier } 1375e96a66cSDavid du Colombier n -= nn; 1385e96a66cSDavid du Colombier offset += nn; 1395e96a66cSDavid du Colombier buf += nn; 1405e96a66cSDavid du Colombier } 1415e96a66cSDavid du Colombier return 1; 1425e96a66cSDavid du Colombier } 1435e96a66cSDavid du Colombier 1445e96a66cSDavid du Colombier int 1455e96a66cSDavid du Colombier diskWriteRaw(Disk *disk, int part, u32int addr, uchar *buf) 1465e96a66cSDavid du Colombier { 1475e96a66cSDavid du Colombier ulong start, end; 1485e96a66cSDavid du Colombier u64int offset; 1495e96a66cSDavid du Colombier int n; 1505e96a66cSDavid du Colombier 1515e96a66cSDavid du Colombier start = partStart(disk, part); 1525e96a66cSDavid du Colombier end = partEnd(disk, part); 1535e96a66cSDavid du Colombier 1545e96a66cSDavid du Colombier if(addr >= end-start){ 1555e96a66cSDavid du Colombier vtSetError(EBadAddr); 1565e96a66cSDavid du Colombier return 0; 1575e96a66cSDavid du Colombier } 1585e96a66cSDavid du Colombier 1595e96a66cSDavid du Colombier offset = ((u64int)(addr + start))*disk->h.blockSize; 160*6de6ce84SDavid du Colombier n = pwrite(disk->fd, buf, disk->h.blockSize, offset); 161*6de6ce84SDavid du Colombier if(n < 0){ 1625e96a66cSDavid du Colombier vtOSError(); 1635e96a66cSDavid du Colombier return 0; 1645e96a66cSDavid du Colombier } 165*6de6ce84SDavid du Colombier if(n < disk->h.blockSize) { 166*6de6ce84SDavid du Colombier vtSetError("short write"); 167*6de6ce84SDavid du Colombier return 0; 168*6de6ce84SDavid du Colombier } 1695e96a66cSDavid du Colombier 1705e96a66cSDavid du Colombier return 1; 1715e96a66cSDavid du Colombier } 1725e96a66cSDavid du Colombier 1735e96a66cSDavid du Colombier static void 1745e96a66cSDavid du Colombier diskQueue(Disk *disk, Block *b) 1755e96a66cSDavid du Colombier { 1765e96a66cSDavid du Colombier Block **bp, *bb; 1775e96a66cSDavid du Colombier 1785e96a66cSDavid du Colombier vtLock(disk->lk); 1795e96a66cSDavid du Colombier while(disk->nqueue >= QueueSize) 1805e96a66cSDavid du Colombier vtSleep(disk->flow); 1815e96a66cSDavid du Colombier if(disk->cur == nil || b->addr > disk->cur->addr) 1825e96a66cSDavid du Colombier bp = &disk->cur; 1835e96a66cSDavid du Colombier else 1845e96a66cSDavid du Colombier bp = &disk->next; 1855e96a66cSDavid du Colombier 1865e96a66cSDavid du Colombier for(bb=*bp; bb; bb=*bp){ 1875e96a66cSDavid du Colombier if(b->addr < bb->addr) 1885e96a66cSDavid du Colombier break; 1895e96a66cSDavid du Colombier bp = &bb->ionext; 1905e96a66cSDavid du Colombier } 1915e96a66cSDavid du Colombier b->ionext = bb; 1925e96a66cSDavid du Colombier *bp = b; 1935e96a66cSDavid du Colombier if(disk->nqueue == 0) 1945e96a66cSDavid du Colombier vtWakeup(disk->starve); 1955e96a66cSDavid du Colombier disk->nqueue++; 1965e96a66cSDavid du Colombier vtUnlock(disk->lk); 1975e96a66cSDavid du Colombier } 1985e96a66cSDavid du Colombier 1995e96a66cSDavid du Colombier 2005e96a66cSDavid du Colombier void 2015e96a66cSDavid du Colombier diskRead(Disk *disk, Block *b) 2025e96a66cSDavid du Colombier { 2035e96a66cSDavid du Colombier assert(b->iostate == BioEmpty || b->iostate == BioLabel); 2045e96a66cSDavid du Colombier blockSetIOState(b, BioReading); 2055e96a66cSDavid du Colombier diskQueue(disk, b); 2065e96a66cSDavid du Colombier } 2075e96a66cSDavid du Colombier 2085e96a66cSDavid du Colombier void 2095e96a66cSDavid du Colombier diskWrite(Disk *disk, Block *b) 2105e96a66cSDavid du Colombier { 2115e96a66cSDavid du Colombier assert(b->iostate == BioDirty); 2125e96a66cSDavid du Colombier blockSetIOState(b, BioWriting); 2135e96a66cSDavid du Colombier diskQueue(disk, b); 2145e96a66cSDavid du Colombier } 2155e96a66cSDavid du Colombier 2165e96a66cSDavid du Colombier int 2175e96a66cSDavid du Colombier diskBlockSize(Disk *disk) 2185e96a66cSDavid du Colombier { 2195e96a66cSDavid du Colombier return disk->h.blockSize; /* immuttable */ 2205e96a66cSDavid du Colombier } 2215e96a66cSDavid du Colombier 2225e96a66cSDavid du Colombier int 2235e96a66cSDavid du Colombier diskFlush(Disk *disk) 2245e96a66cSDavid du Colombier { 2255e96a66cSDavid du Colombier Dir dir; 2265e96a66cSDavid du Colombier 2275e96a66cSDavid du Colombier vtLock(disk->lk); 2285e96a66cSDavid du Colombier while(disk->nqueue > 0) 2295e96a66cSDavid du Colombier vtSleep(disk->flush); 2305e96a66cSDavid du Colombier vtUnlock(disk->lk); 2315e96a66cSDavid du Colombier 2325e96a66cSDavid du Colombier /* there really should be a cleaner interface to flush an fd */ 2335e96a66cSDavid du Colombier nulldir(&dir); 2345e96a66cSDavid du Colombier if(dirfwstat(disk->fd, &dir) < 0){ 2355e96a66cSDavid du Colombier vtOSError(); 2365e96a66cSDavid du Colombier return 0; 2375e96a66cSDavid du Colombier } 2385e96a66cSDavid du Colombier return 1; 2395e96a66cSDavid du Colombier } 2405e96a66cSDavid du Colombier 2415e96a66cSDavid du Colombier u32int 2425e96a66cSDavid du Colombier diskSize(Disk *disk, int part) 2435e96a66cSDavid du Colombier { 2445e96a66cSDavid du Colombier return partEnd(disk, part) - partStart(disk, part); 2455e96a66cSDavid du Colombier } 2465e96a66cSDavid du Colombier 2475e96a66cSDavid du Colombier static void 2485e96a66cSDavid du Colombier diskThread(void *a) 2495e96a66cSDavid du Colombier { 2505e96a66cSDavid du Colombier Disk *disk = a; 2515e96a66cSDavid du Colombier Block *b; 2525e96a66cSDavid du Colombier uchar *buf, *p; 2535e96a66cSDavid du Colombier double t; 2545e96a66cSDavid du Colombier int nio; 2555e96a66cSDavid du Colombier 2565e96a66cSDavid du Colombier vtThreadSetName("disk"); 2575e96a66cSDavid du Colombier 2585e96a66cSDavid du Colombier fprint(2, "diskThread %d\n", getpid()); 2595e96a66cSDavid du Colombier 2605e96a66cSDavid du Colombier buf = vtMemAlloc(disk->h.blockSize); 2615e96a66cSDavid du Colombier 2625e96a66cSDavid du Colombier vtLock(disk->lk); 2635e96a66cSDavid du Colombier nio = 0; 2645e96a66cSDavid du Colombier t = -nsec(); 2655e96a66cSDavid du Colombier for(;;){ 2665e96a66cSDavid du Colombier while(disk->nqueue == 0){ 2675e96a66cSDavid du Colombier t += nsec(); 2685e96a66cSDavid du Colombier if(nio >= 10000){ 2695e96a66cSDavid du Colombier fprint(2, "disk: io=%d at %.3fms\n", nio, t*1e-6/nio); 2705e96a66cSDavid du Colombier nio = 0; 2715e96a66cSDavid du Colombier t = 0.; 2725e96a66cSDavid du Colombier } 2735e96a66cSDavid du Colombier if(disk->die != nil) 2745e96a66cSDavid du Colombier goto Done; 2755e96a66cSDavid du Colombier vtSleep(disk->starve); 2765e96a66cSDavid du Colombier t -= nsec(); 2775e96a66cSDavid du Colombier } 2785e96a66cSDavid du Colombier assert(disk->cur != nil || disk->next != nil); 2795e96a66cSDavid du Colombier 2805e96a66cSDavid du Colombier if(disk->cur == nil){ 2815e96a66cSDavid du Colombier disk->cur = disk->next; 2825e96a66cSDavid du Colombier disk->next = nil; 2835e96a66cSDavid du Colombier } 2845e96a66cSDavid du Colombier b = disk->cur; 2855e96a66cSDavid du Colombier disk->cur = b->ionext; 2865e96a66cSDavid du Colombier vtUnlock(disk->lk); 2875e96a66cSDavid du Colombier 2885e96a66cSDavid du Colombier /* 2895e96a66cSDavid du Colombier * no one should hold onto blocking in the 2905e96a66cSDavid du Colombier * reading or writing state, so this lock should 2915e96a66cSDavid du Colombier * not cause deadlock. 2925e96a66cSDavid du Colombier */ 2935e96a66cSDavid du Colombier if(0)fprint(2, "diskThread: %d:%d %x\n", getpid(), b->part, b->addr); 2945e96a66cSDavid du Colombier bwatchLock(b); 2955e96a66cSDavid du Colombier vtLock(b->lk); 2965e96a66cSDavid du Colombier assert(b->nlock == 1); 2975e96a66cSDavid du Colombier 2985e96a66cSDavid du Colombier switch(b->iostate){ 2995e96a66cSDavid du Colombier default: 3005e96a66cSDavid du Colombier abort(); 3015e96a66cSDavid du Colombier case BioReading: 3025e96a66cSDavid du Colombier if(!diskReadRaw(disk, b->part, b->addr, b->data)){ 303*6de6ce84SDavid du Colombier fprint(2, "diskReadRaw failed: part=%d addr=%ux: %r\n", b->part, b->addr); 3045e96a66cSDavid du Colombier blockSetIOState(b, BioReadError); 3055e96a66cSDavid du Colombier }else 3065e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 3075e96a66cSDavid du Colombier break; 3085e96a66cSDavid du Colombier case BioWriting: 3095e96a66cSDavid du Colombier p = blockRollback(b, buf); 3105e96a66cSDavid du Colombier if(!diskWriteRaw(disk, b->part, b->addr, p)){ 311*6de6ce84SDavid du Colombier fprint(2, "diskWriteRaw failed: date=%s part=%d addr=%ux: %r\n", ctime(times(0)), b->part, b->addr); 3125e96a66cSDavid du Colombier break; 3135e96a66cSDavid du Colombier } 3145e96a66cSDavid du Colombier if(p != buf) 3155e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 3165e96a66cSDavid du Colombier else 3175e96a66cSDavid du Colombier blockSetIOState(b, BioDirty); 3185e96a66cSDavid du Colombier break; 3195e96a66cSDavid du Colombier } 3205e96a66cSDavid du Colombier 3215e96a66cSDavid du Colombier blockPut(b); /* remove extra reference, unlock */ 3225e96a66cSDavid du Colombier vtLock(disk->lk); 3235e96a66cSDavid du Colombier disk->nqueue--; 3245e96a66cSDavid du Colombier if(disk->nqueue == QueueSize-1) 3255e96a66cSDavid du Colombier vtWakeup(disk->flow); 3265e96a66cSDavid du Colombier if(disk->nqueue == 0) 3275e96a66cSDavid du Colombier vtWakeup(disk->flush); 3285e96a66cSDavid du Colombier nio++; 3295e96a66cSDavid du Colombier } 3305e96a66cSDavid du Colombier Done: 3315e96a66cSDavid du Colombier fprint(2, "diskThread done\n"); 3325e96a66cSDavid du Colombier disk->ref--; 3335e96a66cSDavid du Colombier vtWakeup(disk->die); 3345e96a66cSDavid du Colombier vtUnlock(disk->lk); 3355e96a66cSDavid du Colombier vtMemFree(buf); 3365e96a66cSDavid du Colombier } 337