xref: /plan9-contrib/sys/src/cmd/fossil/disk.c (revision 6de6ce8426e615edbe90ecd2ab9bfbc4247cb465)
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