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).
11 typedef struct DiskCache DiskCache;
12 typedef struct DiskCacheBlock DiskCacheBlock;
19 DiskCacheBlock *lruhead;
20 DiskCacheBlock *lrutail;
34 DiskCacheBlock *lrunext;
35 DiskCacheBlock *lruprev;
40 addtohash(DiskCache *d, DiskCacheBlock *b, u64int offset)
44 if(b->offset != ~(u64int)0){
45 fprint(2, "bad offset in addtohash\n");
49 h = offset % d->nhash;
55 delfromhash(DiskCache *d, DiskCacheBlock *b)
60 if(b->offset == ~(u64int)0)
63 h = b->offset % d->nhash;
64 for(l=&d->h[h]; *l; l=&(*l)->next)
67 b->offset = ~(u64int)0;
70 fprint(2, "delfromhash: didn't find in hash table\n");
75 putmru(DiskCache *d, DiskCacheBlock *b)
78 b->lrunext = d->lruhead;
83 b->lrunext->lruprev = b;
87 putlru(DiskCache *d, DiskCacheBlock *b)
89 b->lruprev = d->lrutail;
95 b->lruprev->lrunext = b;
99 delfromlru(DiskCache *d, DiskCacheBlock *b)
102 b->lruprev->lrunext = b->lrunext;
104 d->lruhead = b->lrunext;
106 b->lrunext->lruprev = b->lruprev;
108 d->lrutail = b->lruprev;
111 static DiskCacheBlock*
120 blockput(b->subblock);
126 static DiskCacheBlock*
127 findblock(DiskCache *d, u64int offset)
132 h = offset % d->nhash;
133 for(b=d->h[h]; b; b=b->next)
134 if(b->offset == offset)
139 static DiskCacheBlock*
140 diskcachereadbig(DiskCache *d, u64int offset)
146 dcb = findblock(d, offset);
148 /*fprint(2, "found %llud in cache %p\n", (uvlong)offset, dcb);*/
158 fprint(2, "diskcacheread: all blocks in use\n");
162 b = diskread(d->subdisk, d->blocksize, offset);
168 /*fprint(2, "read %llud from disk %p\n", (uvlong)offset, dcb); */
171 addtohash(d, dcb, offset);
178 diskcacheblockclose(Block *bb)
180 DiskCacheBlock *b = bb->priv;
190 diskcacheread(Disk *dd, u32int len, u64int offset)
193 DiskCache *d = (DiskCache*)dd;
197 if(offset/d->blocksize != (offset+len-1)/d->blocksize){
198 fprint(2, "diskBigRead: request for block crossing big block boundary\n");
202 b = mallocz(sizeof(Block), 1);
206 frag = offset%d->blocksize;
208 dcb = diskcachereadbig(d, offset-frag);
214 b->_close = diskcacheblockclose;
215 b->data = dcb->subblock->data+frag;
217 dlen = dcb->subblock->len;
218 if(frag+len >= dlen){
226 /*fprint(2, "offset %llud at pointer %p %lux\n", (uvlong)offset, b->data, *(ulong*)(b->data+4)); */
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.
237 diskcachesync(Disk *dd)
239 DiskCache *d = (DiskCache*)dd;
240 DiskCacheBlock *b, *nextb;
244 for(i=0; i<d->nhash; i++){
245 for(b=d->h[i]; b; b=nextb){
248 b->offset = ~(u64int)0;
253 return disksync(d->subdisk);
257 diskcacheclose(Disk *dd)
260 DiskCache *d = (DiskCache*)dd;
262 diskclose(d->subdisk);
263 for(b=d->lruhead; b; b=b->lrunext)
264 blockput(b->subblock);
268 /* needn't be fast */
274 for(i=2; i*i<=n; i++)
281 diskcache(Disk *subdisk, uint blocksize, uint ncache)
288 while(nhash > 1 && !isprime(nhash))
290 d = mallocz(sizeof(DiskCache)+ncache*sizeof(DiskCacheBlock)+nhash*sizeof(DiskCacheBlock*), 1);
294 b = (DiskCacheBlock*)&d[1];
295 d->h = (DiskCacheBlock**)&b[ncache];
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;