xref: /plan9-contrib/sys/src/cmd/9660srv/iobuf.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include "dat.h"
6 #include "fns.h"
7 
8 /*
9  * We used to use 100 i/o buffers of size 2kb (Sectorsize).
10  * Unfortunately, reading 2kb at a time often hopping around
11  * the disk doesn't let us get near the disk bandwidth.
12  *
13  * Based on a trace of iobuf address accesses taken while
14  * tarring up a Plan 9 distribution CD, we now use 16 128kb
15  * buffers.  This works for ISO9660 because data is required
16  * to be laid out contiguously; effectively we're doing agressive
17  * readahead.  Because the buffers are so big and the typical
18  * disk accesses so concentrated, it's okay that we have so few
19  * of them.
20  *
21  * If this is used to access multiple discs at once, it's not clear
22  * how gracefully the scheme degrades, but I'm not convinced
23  * it's worth worrying about.		-rsc
24  */
25 
26 #define	BUFPERCLUST	64	/* 64*Sectorsize = 128kb */
27 #define	NCLUST		16
28 
29 static Ioclust*	iohead;
30 static Ioclust*	iotail;
31 static Ioclust*	getclust(Xdata*, long);
32 static void	putclust(Ioclust*);
33 static void xread(Ioclust*);
34 
35 void
36 iobuf_init(void)
37 {
38 	int i, j, n;
39 	Ioclust *c;
40 	Iobuf *b;
41 	uchar *mem;
42 
43 	n = NCLUST*sizeof(Ioclust)+NCLUST*BUFPERCLUST*(sizeof(Iobuf)+Sectorsize);
44 	mem = sbrk(n);
45 	if(mem == (void*)-1)
46 		panic(0, "iobuf_init");
47 	memset(mem, 0, n);
48 
49 	for(i=0; i<NCLUST; i++){
50 		c = (Ioclust*)mem;
51 		mem += sizeof(Ioclust);
52 		c->addr = -1;
53 		c->prev = iotail;
54 		if(iotail)
55 			iotail->next = c;
56 		iotail = c;
57 		if(iohead == nil)
58 			iohead = c;
59 
60 		c->buf = (Iobuf*)mem;
61 		mem += BUFPERCLUST*sizeof(Iobuf);
62 		c->iobuf = mem;
63 		mem += BUFPERCLUST*Sectorsize;
64 		for(j=0; j<BUFPERCLUST; j++){
65 			b = &c->buf[j];
66 			b->clust = c;
67 			b->addr = -1;
68 			b->iobuf = c->iobuf+j*Sectorsize;
69 		}
70 	}
71 }
72 
73 void
74 purgebuf(Xdata *dev)
75 {
76 	Ioclust *p;
77 
78 	for(p=iohead; p!=nil; p=p->next)
79 		if(p->dev == dev){
80 			p->addr = -1;
81 			p->busy = 0;
82 		}
83 }
84 
85 static Ioclust*
86 getclust(Xdata *dev, long addr)
87 {
88 	Ioclust *c, *f;
89 
90 	f = nil;
91 	for(c=iohead; c; c=c->next){
92 		if(!c->busy)
93 			f = c;
94 		if(c->addr == addr && c->dev == dev){
95 			c->busy++;
96 			return c;
97 		}
98 	}
99 
100 	if(f == nil)
101 		panic(0, "out of buffers");
102 
103 	f->addr = addr;
104 	f->dev = dev;
105 	f->busy++;
106 	if(waserror()){
107 		f->addr = -1;	/* stop caching */
108 		putclust(f);
109 		nexterror();
110 	}
111 	xread(f);
112 	poperror();
113 	return f;
114 }
115 
116 static void
117 putclust(Ioclust *c)
118 {
119 	if(c->busy <= 0)
120 		panic(0, "putbuf");
121 	c->busy--;
122 
123 	/* Link onto head for LRU */
124 	if(c == iohead)
125 		return;
126 	c->prev->next = c->next;
127 
128 	if(c->next)
129 		c->next->prev = c->prev;
130 	else
131 		iotail = c->prev;
132 
133 	c->prev = nil;
134 	c->next = iohead;
135 	iohead->prev = c;
136 	iohead = c;
137 }
138 
139 Iobuf*
140 getbuf(Xdata *dev, long addr)
141 {
142 	int off;
143 	Ioclust *c;
144 
145 	off = addr%BUFPERCLUST;
146 	c = getclust(dev, addr - off);
147 	if(c->nbuf < off){
148 		c->busy--;
149 		error("I/O read error");
150 	}
151 	return &c->buf[off];
152 }
153 
154 void
155 putbuf(Iobuf *b)
156 {
157 	putclust(b->clust);
158 }
159 
160 static void
161 xread(Ioclust *c)
162 {
163 	int n;
164 	vlong addr;
165 	Xdata *dev;
166 
167 	dev = c->dev;
168 	addr = c->addr;
169 	seek(dev->dev, addr*Sectorsize, 0);
170 	n = readn(dev->dev, c->iobuf, BUFPERCLUST*Sectorsize);
171 	if(n < Sectorsize)
172 		error("I/O read error");
173 	c->nbuf = n/Sectorsize;
174 }
175