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