1*5e96a66cSDavid du Colombier #include "stdinc.h" 2*5e96a66cSDavid du Colombier #include "dat.h" 3*5e96a66cSDavid du Colombier #include "fns.h" 4*5e96a66cSDavid du Colombier #include "error.h" 5*5e96a66cSDavid du Colombier 6*5e96a66cSDavid du Colombier static void diskThread(void *a); 7*5e96a66cSDavid du Colombier 8*5e96a66cSDavid du Colombier enum { 9*5e96a66cSDavid du Colombier QueueSize = 100, /* maximum block to queue */ 10*5e96a66cSDavid du Colombier }; 11*5e96a66cSDavid du Colombier 12*5e96a66cSDavid du Colombier struct Disk { 13*5e96a66cSDavid du Colombier VtLock *lk; 14*5e96a66cSDavid du Colombier int ref; 15*5e96a66cSDavid du Colombier 16*5e96a66cSDavid du Colombier int fd; 17*5e96a66cSDavid du Colombier Header h; 18*5e96a66cSDavid du Colombier 19*5e96a66cSDavid du Colombier VtRendez *flow; 20*5e96a66cSDavid du Colombier VtRendez *starve; 21*5e96a66cSDavid du Colombier VtRendez *flush; 22*5e96a66cSDavid du Colombier VtRendez *die; 23*5e96a66cSDavid du Colombier 24*5e96a66cSDavid du Colombier int nqueue; 25*5e96a66cSDavid du Colombier 26*5e96a66cSDavid du Colombier Block *cur; /* block to do on current scan */ 27*5e96a66cSDavid du Colombier Block *next; /* blocks to do next scan */ 28*5e96a66cSDavid du Colombier }; 29*5e96a66cSDavid du Colombier 30*5e96a66cSDavid du Colombier 31*5e96a66cSDavid du Colombier Disk * 32*5e96a66cSDavid du Colombier diskAlloc(int fd) 33*5e96a66cSDavid du Colombier { 34*5e96a66cSDavid du Colombier u8int buf[HeaderSize]; 35*5e96a66cSDavid du Colombier Header h; 36*5e96a66cSDavid du Colombier Disk *disk; 37*5e96a66cSDavid du Colombier 38*5e96a66cSDavid du Colombier if(pread(fd, buf, HeaderSize, HeaderOffset) < HeaderSize){ 39*5e96a66cSDavid du Colombier vtOSError(); 40*5e96a66cSDavid du Colombier return nil; 41*5e96a66cSDavid du Colombier } 42*5e96a66cSDavid du Colombier 43*5e96a66cSDavid du Colombier if(!headerUnpack(&h, buf)) 44*5e96a66cSDavid du Colombier return nil; 45*5e96a66cSDavid du Colombier disk = vtMemAllocZ(sizeof(Disk)); 46*5e96a66cSDavid du Colombier disk->lk = vtLockAlloc(); 47*5e96a66cSDavid du Colombier disk->starve = vtRendezAlloc(disk->lk); 48*5e96a66cSDavid du Colombier disk->flow = vtRendezAlloc(disk->lk); 49*5e96a66cSDavid du Colombier disk->flush = vtRendezAlloc(disk->lk); 50*5e96a66cSDavid du Colombier disk->fd = fd; 51*5e96a66cSDavid du Colombier disk->h = h; 52*5e96a66cSDavid du Colombier 53*5e96a66cSDavid du Colombier disk->ref = 2; 54*5e96a66cSDavid du Colombier vtThread(diskThread, disk); 55*5e96a66cSDavid du Colombier 56*5e96a66cSDavid du Colombier return disk; 57*5e96a66cSDavid du Colombier } 58*5e96a66cSDavid du Colombier 59*5e96a66cSDavid du Colombier void 60*5e96a66cSDavid du Colombier diskFree(Disk *disk) 61*5e96a66cSDavid du Colombier { 62*5e96a66cSDavid du Colombier diskFlush(disk); 63*5e96a66cSDavid du Colombier 64*5e96a66cSDavid du Colombier /* kill slave */ 65*5e96a66cSDavid du Colombier vtLock(disk->lk); 66*5e96a66cSDavid du Colombier disk->die = vtRendezAlloc(disk->lk); 67*5e96a66cSDavid du Colombier vtWakeup(disk->starve); 68*5e96a66cSDavid du Colombier while(disk->ref > 1) 69*5e96a66cSDavid du Colombier vtSleep(disk->die); 70*5e96a66cSDavid du Colombier vtUnlock(disk->lk); 71*5e96a66cSDavid du Colombier vtRendezFree(disk->flow); 72*5e96a66cSDavid du Colombier vtRendezFree(disk->starve); 73*5e96a66cSDavid du Colombier vtRendezFree(disk->die); 74*5e96a66cSDavid du Colombier vtLockFree(disk->lk); 75*5e96a66cSDavid du Colombier close(disk->fd); 76*5e96a66cSDavid du Colombier vtMemFree(disk); 77*5e96a66cSDavid du Colombier } 78*5e96a66cSDavid du Colombier 79*5e96a66cSDavid du Colombier static u32int 80*5e96a66cSDavid du Colombier partStart(Disk *disk, int part) 81*5e96a66cSDavid du Colombier { 82*5e96a66cSDavid du Colombier switch(part){ 83*5e96a66cSDavid du Colombier default: 84*5e96a66cSDavid du Colombier assert(0); 85*5e96a66cSDavid du Colombier case PartSuper: 86*5e96a66cSDavid du Colombier return disk->h.super; 87*5e96a66cSDavid du Colombier case PartLabel: 88*5e96a66cSDavid du Colombier return disk->h.label; 89*5e96a66cSDavid du Colombier case PartData: 90*5e96a66cSDavid du Colombier return disk->h.data; 91*5e96a66cSDavid du Colombier } 92*5e96a66cSDavid du Colombier } 93*5e96a66cSDavid du Colombier 94*5e96a66cSDavid du Colombier 95*5e96a66cSDavid du Colombier static u32int 96*5e96a66cSDavid du Colombier partEnd(Disk *disk, int part) 97*5e96a66cSDavid du Colombier { 98*5e96a66cSDavid du Colombier switch(part){ 99*5e96a66cSDavid du Colombier default: 100*5e96a66cSDavid du Colombier assert(0); 101*5e96a66cSDavid du Colombier case PartSuper: 102*5e96a66cSDavid du Colombier return disk->h.super+1; 103*5e96a66cSDavid du Colombier case PartLabel: 104*5e96a66cSDavid du Colombier return disk->h.data; 105*5e96a66cSDavid du Colombier case PartData: 106*5e96a66cSDavid du Colombier return disk->h.end; 107*5e96a66cSDavid du Colombier } 108*5e96a66cSDavid du Colombier } 109*5e96a66cSDavid du Colombier 110*5e96a66cSDavid du Colombier int 111*5e96a66cSDavid du Colombier diskReadRaw(Disk *disk, int part, u32int addr, uchar *buf) 112*5e96a66cSDavid du Colombier { 113*5e96a66cSDavid du Colombier ulong start, end; 114*5e96a66cSDavid du Colombier u64int offset; 115*5e96a66cSDavid du Colombier int n, nn; 116*5e96a66cSDavid du Colombier 117*5e96a66cSDavid du Colombier start = partStart(disk, part); 118*5e96a66cSDavid du Colombier end = partEnd(disk, part); 119*5e96a66cSDavid du Colombier 120*5e96a66cSDavid du Colombier if(addr >= end-start){ 121*5e96a66cSDavid du Colombier vtSetError(EBadAddr); 122*5e96a66cSDavid du Colombier return 0; 123*5e96a66cSDavid du Colombier } 124*5e96a66cSDavid du Colombier 125*5e96a66cSDavid du Colombier offset = ((u64int)(addr + start))*disk->h.blockSize; 126*5e96a66cSDavid du Colombier n = disk->h.blockSize; 127*5e96a66cSDavid du Colombier while(n > 0){ 128*5e96a66cSDavid du Colombier nn = pread(disk->fd, buf, n, offset); 129*5e96a66cSDavid du Colombier if(nn < 0){ 130*5e96a66cSDavid du Colombier vtOSError(); 131*5e96a66cSDavid du Colombier return 0; 132*5e96a66cSDavid du Colombier } 133*5e96a66cSDavid du Colombier if(nn == 0){ 134*5e96a66cSDavid du Colombier vtSetError(EIO); 135*5e96a66cSDavid du Colombier return 0; 136*5e96a66cSDavid du Colombier } 137*5e96a66cSDavid du Colombier n -= nn; 138*5e96a66cSDavid du Colombier offset += nn; 139*5e96a66cSDavid du Colombier buf += nn; 140*5e96a66cSDavid du Colombier } 141*5e96a66cSDavid du Colombier return 1; 142*5e96a66cSDavid du Colombier } 143*5e96a66cSDavid du Colombier 144*5e96a66cSDavid du Colombier int 145*5e96a66cSDavid du Colombier diskWriteRaw(Disk *disk, int part, u32int addr, uchar *buf) 146*5e96a66cSDavid du Colombier { 147*5e96a66cSDavid du Colombier ulong start, end; 148*5e96a66cSDavid du Colombier u64int offset; 149*5e96a66cSDavid du Colombier int n; 150*5e96a66cSDavid du Colombier 151*5e96a66cSDavid du Colombier start = partStart(disk, part); 152*5e96a66cSDavid du Colombier end = partEnd(disk, part); 153*5e96a66cSDavid du Colombier 154*5e96a66cSDavid du Colombier if(addr >= end-start){ 155*5e96a66cSDavid du Colombier vtSetError(EBadAddr); 156*5e96a66cSDavid du Colombier return 0; 157*5e96a66cSDavid du Colombier } 158*5e96a66cSDavid du Colombier 159*5e96a66cSDavid du Colombier offset = ((u64int)(addr + start))*disk->h.blockSize; 160*5e96a66cSDavid du Colombier n = disk->h.blockSize; 161*5e96a66cSDavid du Colombier if(pwrite(disk->fd, buf, n, offset) < n){ 162*5e96a66cSDavid du Colombier vtOSError(); 163*5e96a66cSDavid du Colombier return 0; 164*5e96a66cSDavid du Colombier } 165*5e96a66cSDavid du Colombier 166*5e96a66cSDavid du Colombier return 1; 167*5e96a66cSDavid du Colombier } 168*5e96a66cSDavid du Colombier 169*5e96a66cSDavid du Colombier static void 170*5e96a66cSDavid du Colombier diskQueue(Disk *disk, Block *b) 171*5e96a66cSDavid du Colombier { 172*5e96a66cSDavid du Colombier Block **bp, *bb; 173*5e96a66cSDavid du Colombier 174*5e96a66cSDavid du Colombier vtLock(disk->lk); 175*5e96a66cSDavid du Colombier while(disk->nqueue >= QueueSize) 176*5e96a66cSDavid du Colombier vtSleep(disk->flow); 177*5e96a66cSDavid du Colombier if(disk->cur == nil || b->addr > disk->cur->addr) 178*5e96a66cSDavid du Colombier bp = &disk->cur; 179*5e96a66cSDavid du Colombier else 180*5e96a66cSDavid du Colombier bp = &disk->next; 181*5e96a66cSDavid du Colombier 182*5e96a66cSDavid du Colombier for(bb=*bp; bb; bb=*bp){ 183*5e96a66cSDavid du Colombier if(b->addr < bb->addr) 184*5e96a66cSDavid du Colombier break; 185*5e96a66cSDavid du Colombier bp = &bb->ionext; 186*5e96a66cSDavid du Colombier } 187*5e96a66cSDavid du Colombier b->ionext = bb; 188*5e96a66cSDavid du Colombier *bp = b; 189*5e96a66cSDavid du Colombier if(disk->nqueue == 0) 190*5e96a66cSDavid du Colombier vtWakeup(disk->starve); 191*5e96a66cSDavid du Colombier disk->nqueue++; 192*5e96a66cSDavid du Colombier vtUnlock(disk->lk); 193*5e96a66cSDavid du Colombier } 194*5e96a66cSDavid du Colombier 195*5e96a66cSDavid du Colombier 196*5e96a66cSDavid du Colombier void 197*5e96a66cSDavid du Colombier diskRead(Disk *disk, Block *b) 198*5e96a66cSDavid du Colombier { 199*5e96a66cSDavid du Colombier assert(b->iostate == BioEmpty || b->iostate == BioLabel); 200*5e96a66cSDavid du Colombier blockSetIOState(b, BioReading); 201*5e96a66cSDavid du Colombier diskQueue(disk, b); 202*5e96a66cSDavid du Colombier } 203*5e96a66cSDavid du Colombier 204*5e96a66cSDavid du Colombier void 205*5e96a66cSDavid du Colombier diskWrite(Disk *disk, Block *b) 206*5e96a66cSDavid du Colombier { 207*5e96a66cSDavid du Colombier assert(b->iostate == BioDirty); 208*5e96a66cSDavid du Colombier blockSetIOState(b, BioWriting); 209*5e96a66cSDavid du Colombier diskQueue(disk, b); 210*5e96a66cSDavid du Colombier } 211*5e96a66cSDavid du Colombier 212*5e96a66cSDavid du Colombier int 213*5e96a66cSDavid du Colombier diskBlockSize(Disk *disk) 214*5e96a66cSDavid du Colombier { 215*5e96a66cSDavid du Colombier return disk->h.blockSize; /* immuttable */ 216*5e96a66cSDavid du Colombier } 217*5e96a66cSDavid du Colombier 218*5e96a66cSDavid du Colombier int 219*5e96a66cSDavid du Colombier diskFlush(Disk *disk) 220*5e96a66cSDavid du Colombier { 221*5e96a66cSDavid du Colombier Dir dir; 222*5e96a66cSDavid du Colombier 223*5e96a66cSDavid du Colombier vtLock(disk->lk); 224*5e96a66cSDavid du Colombier while(disk->nqueue > 0) 225*5e96a66cSDavid du Colombier vtSleep(disk->flush); 226*5e96a66cSDavid du Colombier vtUnlock(disk->lk); 227*5e96a66cSDavid du Colombier 228*5e96a66cSDavid du Colombier /* there really should be a cleaner interface to flush an fd */ 229*5e96a66cSDavid du Colombier nulldir(&dir); 230*5e96a66cSDavid du Colombier if(dirfwstat(disk->fd, &dir) < 0){ 231*5e96a66cSDavid du Colombier vtOSError(); 232*5e96a66cSDavid du Colombier return 0; 233*5e96a66cSDavid du Colombier } 234*5e96a66cSDavid du Colombier return 1; 235*5e96a66cSDavid du Colombier } 236*5e96a66cSDavid du Colombier 237*5e96a66cSDavid du Colombier u32int 238*5e96a66cSDavid du Colombier diskSize(Disk *disk, int part) 239*5e96a66cSDavid du Colombier { 240*5e96a66cSDavid du Colombier return partEnd(disk, part) - partStart(disk, part); 241*5e96a66cSDavid du Colombier } 242*5e96a66cSDavid du Colombier 243*5e96a66cSDavid du Colombier static void 244*5e96a66cSDavid du Colombier diskThread(void *a) 245*5e96a66cSDavid du Colombier { 246*5e96a66cSDavid du Colombier Disk *disk = a; 247*5e96a66cSDavid du Colombier Block *b; 248*5e96a66cSDavid du Colombier uchar *buf, *p; 249*5e96a66cSDavid du Colombier double t; 250*5e96a66cSDavid du Colombier int nio; 251*5e96a66cSDavid du Colombier 252*5e96a66cSDavid du Colombier vtThreadSetName("disk"); 253*5e96a66cSDavid du Colombier 254*5e96a66cSDavid du Colombier fprint(2, "diskThread %d\n", getpid()); 255*5e96a66cSDavid du Colombier 256*5e96a66cSDavid du Colombier buf = vtMemAlloc(disk->h.blockSize); 257*5e96a66cSDavid du Colombier 258*5e96a66cSDavid du Colombier vtLock(disk->lk); 259*5e96a66cSDavid du Colombier nio = 0; 260*5e96a66cSDavid du Colombier t = -nsec(); 261*5e96a66cSDavid du Colombier for(;;){ 262*5e96a66cSDavid du Colombier while(disk->nqueue == 0){ 263*5e96a66cSDavid du Colombier t += nsec(); 264*5e96a66cSDavid du Colombier if(nio >= 10000){ 265*5e96a66cSDavid du Colombier fprint(2, "disk: io=%d at %.3fms\n", nio, t*1e-6/nio); 266*5e96a66cSDavid du Colombier nio = 0; 267*5e96a66cSDavid du Colombier t = 0.; 268*5e96a66cSDavid du Colombier } 269*5e96a66cSDavid du Colombier if(disk->die != nil) 270*5e96a66cSDavid du Colombier goto Done; 271*5e96a66cSDavid du Colombier vtSleep(disk->starve); 272*5e96a66cSDavid du Colombier t -= nsec(); 273*5e96a66cSDavid du Colombier } 274*5e96a66cSDavid du Colombier assert(disk->cur != nil || disk->next != nil); 275*5e96a66cSDavid du Colombier 276*5e96a66cSDavid du Colombier if(disk->cur == nil){ 277*5e96a66cSDavid du Colombier disk->cur = disk->next; 278*5e96a66cSDavid du Colombier disk->next = nil; 279*5e96a66cSDavid du Colombier } 280*5e96a66cSDavid du Colombier b = disk->cur; 281*5e96a66cSDavid du Colombier disk->cur = b->ionext; 282*5e96a66cSDavid du Colombier vtUnlock(disk->lk); 283*5e96a66cSDavid du Colombier 284*5e96a66cSDavid du Colombier /* 285*5e96a66cSDavid du Colombier * no one should hold onto blocking in the 286*5e96a66cSDavid du Colombier * reading or writing state, so this lock should 287*5e96a66cSDavid du Colombier * not cause deadlock. 288*5e96a66cSDavid du Colombier */ 289*5e96a66cSDavid du Colombier if(0)fprint(2, "diskThread: %d:%d %x\n", getpid(), b->part, b->addr); 290*5e96a66cSDavid du Colombier bwatchLock(b); 291*5e96a66cSDavid du Colombier vtLock(b->lk); 292*5e96a66cSDavid du Colombier assert(b->nlock == 1); 293*5e96a66cSDavid du Colombier 294*5e96a66cSDavid du Colombier switch(b->iostate){ 295*5e96a66cSDavid du Colombier default: 296*5e96a66cSDavid du Colombier abort(); 297*5e96a66cSDavid du Colombier case BioReading: 298*5e96a66cSDavid du Colombier if(!diskReadRaw(disk, b->part, b->addr, b->data)){ 299*5e96a66cSDavid du Colombier fprint(2, "diskReadRaw failed: part=%d addr=%ux: %r", b->part, b->addr); 300*5e96a66cSDavid du Colombier blockSetIOState(b, BioReadError); 301*5e96a66cSDavid du Colombier }else 302*5e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 303*5e96a66cSDavid du Colombier break; 304*5e96a66cSDavid du Colombier case BioWriting: 305*5e96a66cSDavid du Colombier p = blockRollback(b, buf); 306*5e96a66cSDavid du Colombier if(!diskWriteRaw(disk, b->part, b->addr, p)){ 307*5e96a66cSDavid du Colombier fprint(2, "diskWriteRaw failed: part=%d addr=%ux: %r", b->part, b->addr); 308*5e96a66cSDavid du Colombier break; 309*5e96a66cSDavid du Colombier } 310*5e96a66cSDavid du Colombier if(p != buf) 311*5e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 312*5e96a66cSDavid du Colombier else 313*5e96a66cSDavid du Colombier blockSetIOState(b, BioDirty); 314*5e96a66cSDavid du Colombier break; 315*5e96a66cSDavid du Colombier } 316*5e96a66cSDavid du Colombier 317*5e96a66cSDavid du Colombier blockPut(b); /* remove extra reference, unlock */ 318*5e96a66cSDavid du Colombier vtLock(disk->lk); 319*5e96a66cSDavid du Colombier disk->nqueue--; 320*5e96a66cSDavid du Colombier if(disk->nqueue == QueueSize-1) 321*5e96a66cSDavid du Colombier vtWakeup(disk->flow); 322*5e96a66cSDavid du Colombier if(disk->nqueue == 0) 323*5e96a66cSDavid du Colombier vtWakeup(disk->flush); 324*5e96a66cSDavid du Colombier nio++; 325*5e96a66cSDavid du Colombier } 326*5e96a66cSDavid du Colombier Done: 327*5e96a66cSDavid du Colombier fprint(2, "diskThread done\n"); 328*5e96a66cSDavid du Colombier disk->ref--; 329*5e96a66cSDavid du Colombier vtWakeup(disk->die); 330*5e96a66cSDavid du Colombier vtUnlock(disk->lk); 331*5e96a66cSDavid du Colombier vtMemFree(buf); 332*5e96a66cSDavid du Colombier } 333