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 { 2145e96a66cSDavid du Colombier assert(b->iostate == BioDirty); 2155e96a66cSDavid du Colombier blockSetIOState(b, BioWriting); 2165e96a66cSDavid du Colombier diskQueue(disk, b); 2175e96a66cSDavid du Colombier } 2185e96a66cSDavid du Colombier 2195e96a66cSDavid du Colombier int 2205e96a66cSDavid du Colombier diskBlockSize(Disk *disk) 2215e96a66cSDavid du Colombier { 2225e96a66cSDavid du Colombier return disk->h.blockSize; /* immuttable */ 2235e96a66cSDavid du Colombier } 2245e96a66cSDavid du Colombier 2255e96a66cSDavid du Colombier int 2265e96a66cSDavid du Colombier diskFlush(Disk *disk) 2275e96a66cSDavid du Colombier { 2285e96a66cSDavid du Colombier Dir dir; 2295e96a66cSDavid du Colombier 2305e96a66cSDavid du Colombier vtLock(disk->lk); 2315e96a66cSDavid du Colombier while(disk->nqueue > 0) 2325e96a66cSDavid du Colombier vtSleep(disk->flush); 2335e96a66cSDavid du Colombier vtUnlock(disk->lk); 2345e96a66cSDavid du Colombier 2355e96a66cSDavid du Colombier /* there really should be a cleaner interface to flush an fd */ 2365e96a66cSDavid du Colombier nulldir(&dir); 2375e96a66cSDavid du Colombier if(dirfwstat(disk->fd, &dir) < 0){ 2385e96a66cSDavid du Colombier vtOSError(); 2395e96a66cSDavid du Colombier return 0; 2405e96a66cSDavid du Colombier } 2415e96a66cSDavid du Colombier return 1; 2425e96a66cSDavid du Colombier } 2435e96a66cSDavid du Colombier 2445e96a66cSDavid du Colombier u32int 2455e96a66cSDavid du Colombier diskSize(Disk *disk, int part) 2465e96a66cSDavid du Colombier { 2475e96a66cSDavid du Colombier return partEnd(disk, part) - partStart(disk, part); 2485e96a66cSDavid du Colombier } 2495e96a66cSDavid du Colombier 2505e96a66cSDavid du Colombier static void 2515e96a66cSDavid du Colombier diskThread(void *a) 2525e96a66cSDavid du Colombier { 2535e96a66cSDavid du Colombier Disk *disk = a; 2545e96a66cSDavid du Colombier Block *b; 2555e96a66cSDavid du Colombier uchar *buf, *p; 2565e96a66cSDavid du Colombier double t; 2575e96a66cSDavid du Colombier int nio; 2585e96a66cSDavid du Colombier 2595e96a66cSDavid du Colombier vtThreadSetName("disk"); 2605e96a66cSDavid du Colombier 261dc5a79c1SDavid du Colombier //fprint(2, "diskThread %d\n", getpid()); 2625e96a66cSDavid du Colombier 2635e96a66cSDavid du Colombier buf = vtMemAlloc(disk->h.blockSize); 2645e96a66cSDavid du Colombier 2655e96a66cSDavid du Colombier vtLock(disk->lk); 2665e96a66cSDavid du Colombier nio = 0; 2675e96a66cSDavid du Colombier t = -nsec(); 2685e96a66cSDavid du Colombier for(;;){ 2695e96a66cSDavid du Colombier while(disk->nqueue == 0){ 2705e96a66cSDavid du Colombier t += nsec(); 271*39734e7eSDavid du Colombier //if(nio >= 10000){ 272*39734e7eSDavid du Colombier //fprint(2, "disk: io=%d at %.3fms\n", nio, t*1e-6/nio); 273*39734e7eSDavid du Colombier //nio = 0; 274*39734e7eSDavid du Colombier //t = 0.; 275*39734e7eSDavid du Colombier //} 2765e96a66cSDavid du Colombier if(disk->die != nil) 2775e96a66cSDavid du Colombier goto Done; 2785e96a66cSDavid du Colombier vtSleep(disk->starve); 2795e96a66cSDavid du Colombier t -= nsec(); 2805e96a66cSDavid du Colombier } 2815e96a66cSDavid du Colombier assert(disk->cur != nil || disk->next != nil); 2825e96a66cSDavid du Colombier 2835e96a66cSDavid du Colombier if(disk->cur == nil){ 2845e96a66cSDavid du Colombier disk->cur = disk->next; 2855e96a66cSDavid du Colombier disk->next = nil; 2865e96a66cSDavid du Colombier } 2875e96a66cSDavid du Colombier b = disk->cur; 2885e96a66cSDavid du Colombier disk->cur = b->ionext; 2895e96a66cSDavid du Colombier vtUnlock(disk->lk); 2905e96a66cSDavid du Colombier 2915e96a66cSDavid du Colombier /* 2925e96a66cSDavid du Colombier * no one should hold onto blocking in the 2935e96a66cSDavid du Colombier * reading or writing state, so this lock should 2945e96a66cSDavid du Colombier * not cause deadlock. 2955e96a66cSDavid du Colombier */ 2965e96a66cSDavid du Colombier if(0)fprint(2, "diskThread: %d:%d %x\n", getpid(), b->part, b->addr); 2975e96a66cSDavid du Colombier bwatchLock(b); 2985e96a66cSDavid du Colombier vtLock(b->lk); 2995e96a66cSDavid du Colombier assert(b->nlock == 1); 3005e96a66cSDavid du Colombier 3015e96a66cSDavid du Colombier switch(b->iostate){ 3025e96a66cSDavid du Colombier default: 3035e96a66cSDavid du Colombier abort(); 3045e96a66cSDavid du Colombier case BioReading: 3055e96a66cSDavid du Colombier if(!diskReadRaw(disk, b->part, b->addr, b->data)){ 3066de6ce84SDavid du Colombier fprint(2, "diskReadRaw failed: part=%d addr=%ux: %r\n", b->part, b->addr); 3075e96a66cSDavid du Colombier blockSetIOState(b, BioReadError); 3085e96a66cSDavid du Colombier }else 3095e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 3105e96a66cSDavid du Colombier break; 3115e96a66cSDavid du Colombier case BioWriting: 3125e96a66cSDavid du Colombier p = blockRollback(b, buf); 3135e96a66cSDavid du Colombier if(!diskWriteRaw(disk, b->part, b->addr, p)){ 3146de6ce84SDavid du Colombier fprint(2, "diskWriteRaw failed: date=%s part=%d addr=%ux: %r\n", ctime(times(0)), b->part, b->addr); 3155e96a66cSDavid du Colombier break; 3165e96a66cSDavid du Colombier } 3175e96a66cSDavid du Colombier if(p != buf) 3185e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 3195e96a66cSDavid du Colombier else 3205e96a66cSDavid du Colombier blockSetIOState(b, BioDirty); 3215e96a66cSDavid du Colombier break; 3225e96a66cSDavid du Colombier } 3235e96a66cSDavid du Colombier 3245e96a66cSDavid du Colombier blockPut(b); /* remove extra reference, unlock */ 3255e96a66cSDavid du Colombier vtLock(disk->lk); 3265e96a66cSDavid du Colombier disk->nqueue--; 3275e96a66cSDavid du Colombier if(disk->nqueue == QueueSize-1) 3285e96a66cSDavid du Colombier vtWakeup(disk->flow); 3295e96a66cSDavid du Colombier if(disk->nqueue == 0) 3305e96a66cSDavid du Colombier vtWakeup(disk->flush); 3315e96a66cSDavid du Colombier nio++; 3325e96a66cSDavid du Colombier } 3335e96a66cSDavid du Colombier Done: 334dc5a79c1SDavid du Colombier //fprint(2, "diskThread done\n"); 3355e96a66cSDavid du Colombier disk->ref--; 3365e96a66cSDavid du Colombier vtWakeup(disk->die); 3375e96a66cSDavid du Colombier vtUnlock(disk->lk); 3385e96a66cSDavid du Colombier vtMemFree(buf); 3395e96a66cSDavid du Colombier } 340