Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <diskfs.h>
5 /*
6 * Disk cache. Caches by offset, so higher levels have
7 * to deal with alignment issues (if we get asked for the
8 * blocks at offsets 0 and 1, we'll do two reads).
9 */
11 typedef struct DiskCache DiskCache;
12 typedef struct DiskCacheBlock DiskCacheBlock;
14 struct DiskCache
15 {
16 Disk disk;
17 Disk *subdisk;
18 DiskCacheBlock **h;
19 DiskCacheBlock *lruhead;
20 DiskCacheBlock *lrutail;
21 int nhash;
22 int blocksize;
23 Lock lk;
24 };
26 struct DiskCacheBlock
27 {
28 Block block;
29 Block *subblock;
30 Lock lk;
31 int ref;
32 DiskCache *dc;
33 DiskCacheBlock *next;
34 DiskCacheBlock *lrunext;
35 DiskCacheBlock *lruprev;
36 u64int offset;
37 };
39 static void
40 addtohash(DiskCache *d, DiskCacheBlock *b, u64int offset)
41 {
42 int h;
44 if(b->offset != ~(u64int)0){
45 fprint(2, "bad offset in addtohash\n");
46 return;
47 }
48 b->offset = offset;
49 h = offset % d->nhash;
50 b->next = d->h[h];
51 d->h[h] = b;
52 }
54 static void
55 delfromhash(DiskCache *d, DiskCacheBlock *b)
56 {
57 int h;
58 DiskCacheBlock **l;
60 if(b->offset == ~(u64int)0)
61 return;
63 h = b->offset % d->nhash;
64 for(l=&d->h[h]; *l; l=&(*l)->next)
65 if(*l == b){
66 *l = b->next;
67 b->offset = ~(u64int)0;
68 return;
69 }
70 fprint(2, "delfromhash: didn't find in hash table\n");
71 return;
72 }
74 static void
75 putmru(DiskCache *d, DiskCacheBlock *b)
76 {
77 b->lruprev = nil;
78 b->lrunext = d->lruhead;
79 d->lruhead = b;
80 if(b->lrunext == nil)
81 d->lrutail = b;
82 else
83 b->lrunext->lruprev = b;
84 }
86 static void
87 putlru(DiskCache *d, DiskCacheBlock *b)
88 {
89 b->lruprev = d->lrutail;
90 b->lrunext = nil;
91 d->lrutail = b;
92 if(b->lruprev == nil)
93 d->lruhead = b;
94 else
95 b->lruprev->lrunext = b;
96 }
98 static void
99 delfromlru(DiskCache *d, DiskCacheBlock *b)
101 if(b->lruprev)
102 b->lruprev->lrunext = b->lrunext;
103 else
104 d->lruhead = b->lrunext;
105 if(b->lrunext)
106 b->lrunext->lruprev = b->lruprev;
107 else
108 d->lrutail = b->lruprev;
111 static DiskCacheBlock*
112 getlru(DiskCache *d)
114 DiskCacheBlock *b;
116 b = d->lrutail;
117 if(b){
118 delfromlru(d, b);
119 delfromhash(d, b);
120 blockput(b->subblock);
121 b->subblock = nil;
123 return b;
126 static DiskCacheBlock*
127 findblock(DiskCache *d, u64int offset)
129 int h;
130 DiskCacheBlock *b;
132 h = offset % d->nhash;
133 for(b=d->h[h]; b; b=b->next)
134 if(b->offset == offset)
135 return b;
136 return nil;
139 static DiskCacheBlock*
140 diskcachereadbig(DiskCache *d, u64int offset)
142 Block *b;
143 DiskCacheBlock *dcb;
145 lock(&d->lk);
146 dcb = findblock(d, offset);
147 if(dcb){
148 /*fprint(2, "found %llud in cache %p\n", (uvlong)offset, dcb);*/
149 if(dcb->ref++ == 0)
150 delfromlru(d, dcb);
151 unlock(&d->lk);
152 return dcb;
155 dcb = getlru(d);
156 unlock(&d->lk);
157 if(dcb == nil){
158 fprint(2, "diskcacheread: all blocks in use\n");
159 return nil;
162 b = diskread(d->subdisk, d->blocksize, offset);
163 lock(&d->lk);
164 if(b == nil){
165 putlru(d, dcb);
166 dcb = nil;
167 }else{
168 /*fprint(2, "read %llud from disk %p\n", (uvlong)offset, dcb); */
169 dcb->subblock = b;
170 dcb->ref++;
171 addtohash(d, dcb, offset);
173 unlock(&d->lk);
174 return dcb;
177 static void
178 diskcacheblockclose(Block *bb)
180 DiskCacheBlock *b = bb->priv;
182 lock(&b->dc->lk);
183 if(--b->ref == 0)
184 putmru(b->dc, b);
185 unlock(&b->dc->lk);
186 free(bb);
189 static Block*
190 diskcacheread(Disk *dd, u32int len, u64int offset)
192 int frag, dlen;
193 DiskCache *d = (DiskCache*)dd;
194 DiskCacheBlock *dcb;
195 Block *b;
197 if(offset/d->blocksize != (offset+len-1)/d->blocksize){
198 fprint(2, "diskBigRead: request for block crossing big block boundary\n");
199 return nil;
202 b = mallocz(sizeof(Block), 1);
203 if(b == nil)
204 return nil;
206 frag = offset%d->blocksize;
208 dcb = diskcachereadbig(d, offset-frag);
209 if(dcb == nil){
210 free(b);
211 return nil;
213 b->priv = dcb;
214 b->_close = diskcacheblockclose;
215 b->data = dcb->subblock->data+frag;
217 dlen = dcb->subblock->len;
218 if(frag+len >= dlen){
219 if(frag >= dlen){
220 blockput(b);
221 return nil;
223 len = dlen-frag;
225 b->len = len;
226 /*fprint(2, "offset %llud at pointer %p %lux\n", (uvlong)offset, b->data, *(ulong*)(b->data+4)); */
227 return b;
230 /*
231 * It's okay to remove these from the hash table.
232 * Either the block is in use by someone or it is on
233 * the lru list. If it's in use it will get put on the lru
234 * list once the refs go away.
235 */
236 static int
237 diskcachesync(Disk *dd)
239 DiskCache *d = (DiskCache*)dd;
240 DiskCacheBlock *b, *nextb;
241 int i;
243 lock(&d->lk);
244 for(i=0; i<d->nhash; i++){
245 for(b=d->h[i]; b; b=nextb){
246 nextb = b->next;
247 b->next = nil;
248 b->offset = ~(u64int)0;
250 d->h[i] = nil;
252 unlock(&d->lk);
253 return disksync(d->subdisk);
256 static void
257 diskcacheclose(Disk *dd)
259 DiskCacheBlock *b;
260 DiskCache *d = (DiskCache*)dd;
262 diskclose(d->subdisk);
263 for(b=d->lruhead; b; b=b->lrunext)
264 blockput(b->subblock);
265 free(d);
268 /* needn't be fast */
269 static int
270 isprime(int n)
272 int i;
274 for(i=2; i*i<=n; i++)
275 if(n%i == 0)
276 return 0;
277 return 1;
280 Disk*
281 diskcache(Disk *subdisk, uint blocksize, uint ncache)
283 int nhash, i;
284 DiskCache *d;
285 DiskCacheBlock *b;
287 nhash = ncache;
288 while(nhash > 1 && !isprime(nhash))
289 nhash--;
290 d = mallocz(sizeof(DiskCache)+ncache*sizeof(DiskCacheBlock)+nhash*sizeof(DiskCacheBlock*), 1);
291 if(d == nil)
292 return nil;
294 b = (DiskCacheBlock*)&d[1];
295 d->h = (DiskCacheBlock**)&b[ncache];
296 d->nhash = nhash;
297 d->blocksize = blocksize;
298 d->subdisk = subdisk;
299 d->disk._read = diskcacheread;
300 d->disk._sync = diskcachesync;
301 d->disk._close = diskcacheclose;
303 for(i=0; i<ncache; i++){
304 b[i].block._close = diskcacheblockclose;
305 b[i].offset = ~(u64int)0;
306 b[i].dc = d;
307 putlru(d, &b[i]);
310 return &d->disk;