dcache.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. #include "stdinc.h"
  2. #include "dat.h"
  3. #include "fns.h"
  4. typedef struct DCache DCache;
  5. enum
  6. {
  7. HashLog = 9,
  8. HashSize = 1<<HashLog,
  9. HashMask = HashSize - 1,
  10. };
  11. struct DCache
  12. {
  13. VtLock *lock;
  14. VtRendez *full;
  15. DBlock *free; /* list of available lumps */
  16. u32int now; /* ticks for usage timestamps */
  17. int size; /* max. size of any block; allocated to each block */
  18. DBlock **heads; /* hash table for finding address */
  19. int nheap; /* number of available victims */
  20. DBlock **heap; /* heap for locating victims */
  21. int nblocks; /* number of blocks allocated */
  22. DBlock *blocks; /* array of block descriptors */
  23. u8int *mem; /* memory for all block descriptors */
  24. };
  25. static DCache dCache;
  26. static int downHeap(int i, DBlock *b);
  27. static int upHeap(int i, DBlock *b);
  28. static DBlock *bumpDBlock(void);
  29. static void delHeap(DBlock *db);
  30. static void fixHeap(int i, DBlock *b);
  31. void
  32. initDCache(u32int mem)
  33. {
  34. DBlock *b, *last;
  35. u32int nblocks, blockSize;
  36. int i;
  37. if(mem < maxBlockSize * 2)
  38. fatal("need at least %d bytes for the disk cache", maxBlockSize * 2);
  39. if(maxBlockSize == 0)
  40. fatal("no max. block size given for disk cache");
  41. blockSize = maxBlockSize;
  42. nblocks = mem / blockSize;
  43. if(0)
  44. fprint(2, "initialize disk cache with %d blocks of %d bytes\n", nblocks, blockSize);
  45. dCache.lock = vtLockAlloc();
  46. dCache.full = vtRendezAlloc(dCache.lock);
  47. dCache.nblocks = nblocks;
  48. dCache.size = blockSize;
  49. dCache.heads = MKNZ(DBlock*, HashSize);
  50. dCache.heap = MKNZ(DBlock*, nblocks);
  51. dCache.blocks = MKNZ(DBlock, nblocks);
  52. dCache.mem = MKNZ(u8int, nblocks * blockSize);
  53. last = nil;
  54. for(i = 0; i < nblocks; i++){
  55. b = &dCache.blocks[i];
  56. b->data = &dCache.mem[i * blockSize];
  57. b->heap = TWID32;
  58. b->lock = vtLockAlloc();
  59. b->next = last;
  60. last = b;
  61. }
  62. dCache.free = last;
  63. dCache.nheap = 0;
  64. }
  65. static u32int
  66. pbHash(u64int addr)
  67. {
  68. u32int h;
  69. #define hashit(c) ((((c) * 0x6b43a9b5) >> (32 - HashLog)) & HashMask)
  70. h = (addr >> 32) ^ addr;
  71. return hashit(h);
  72. }
  73. DBlock*
  74. getDBlock(Part *part, u64int addr, int read)
  75. {
  76. DBlock *b;
  77. u32int h, size;
  78. size = part->blockSize;
  79. if(size > dCache.size){
  80. setErr(EAdmin, "block size %d too big for cache", size);
  81. return nil;
  82. }
  83. h = pbHash(addr);
  84. /*
  85. * look for the block in the cache
  86. */
  87. //checkDCache();
  88. vtLock(dCache.lock);
  89. again:
  90. for(b = dCache.heads[h]; b != nil; b = b->next){
  91. if(b->part == part && b->addr == addr){
  92. vtLock(stats.lock);
  93. stats.pcHit++;
  94. vtUnlock(stats.lock);
  95. goto found;
  96. }
  97. }
  98. vtLock(stats.lock);
  99. stats.pcMiss++;
  100. vtUnlock(stats.lock);
  101. /*
  102. * missed: locate the block with the oldest second to last use.
  103. * remove it from the heap, and fix up the heap.
  104. */
  105. b = bumpDBlock();
  106. if(b == nil){
  107. logErr(EAdmin, "all disk cache blocks in use");
  108. vtSleep(dCache.full);
  109. goto again;
  110. }
  111. /*
  112. * the new block has no last use, so assume it happens sometime in the middle
  113. ZZZ this is not reasonable
  114. */
  115. b->used = (b->used2 + dCache.now) / 2;
  116. /*
  117. * rechain the block on the correct hash chain
  118. */
  119. b->next = dCache.heads[h];
  120. dCache.heads[h] = b;
  121. if(b->next != nil)
  122. b->next->prev = b;
  123. b->prev = nil;
  124. b->addr = addr;
  125. b->part = part;
  126. b->size = 0;
  127. found:
  128. b->ref++;
  129. b->used2 = b->used;
  130. b->used = dCache.now++;
  131. if(b->heap != TWID32)
  132. fixHeap(b->heap, b);
  133. vtUnlock(dCache.lock);
  134. //checkDCache();
  135. vtLock(b->lock);
  136. if(b->size != size){
  137. if(b->size < size){
  138. if(!read)
  139. memset(&b->data[b->size], 0, size - b->size);
  140. else if(readPart(part, addr + b->size, &b->data[b->size], size - b->size)){
  141. vtLock(stats.lock);
  142. stats.pcReads++;
  143. stats.pcBReads += size - b->size;
  144. vtUnlock(stats.lock);
  145. }else{
  146. putDBlock(b);
  147. return nil;
  148. }
  149. }
  150. b->size = size;
  151. }
  152. return b;
  153. }
  154. void
  155. putDBlock(DBlock *b)
  156. {
  157. if(b == nil)
  158. return;
  159. vtUnlock(b->lock);
  160. //checkDCache();
  161. vtLock(dCache.lock);
  162. if(--b->ref == 0){
  163. if(b->heap == TWID32)
  164. upHeap(dCache.nheap++, b);
  165. vtWakeup(dCache.full);
  166. }
  167. vtUnlock(dCache.lock);
  168. //checkDCache();
  169. }
  170. /*
  171. * remove some block from use and update the free list and counters
  172. */
  173. static DBlock*
  174. bumpDBlock(void)
  175. {
  176. DBlock *b;
  177. ulong h;
  178. b = dCache.free;
  179. if(b != nil){
  180. dCache.free = b->next;
  181. return b;
  182. }
  183. /*
  184. * remove blocks until we find one that is unused
  185. * referenced blocks are left in the heap even though
  186. * they can't be scavenged; this is simple a speed optimization
  187. */
  188. for(;;){
  189. if(dCache.nheap == 0)
  190. return nil;
  191. b = dCache.heap[0];
  192. delHeap(b);
  193. if(!b->ref)
  194. break;
  195. }
  196. /*
  197. * unchain the block
  198. */
  199. if(b->prev == nil){
  200. h = pbHash(b->addr);
  201. if(dCache.heads[h] != b)
  202. fatal("bad hash chains in disk cache");
  203. dCache.heads[h] = b->next;
  204. }else
  205. b->prev->next = b->next;
  206. if(b->next != nil)
  207. b->next->prev = b->prev;
  208. return b;
  209. }
  210. /*
  211. * delete an arbitrary block from the heap
  212. */
  213. static void
  214. delHeap(DBlock *db)
  215. {
  216. fixHeap(db->heap, dCache.heap[--dCache.nheap]);
  217. db->heap = TWID32;
  218. }
  219. /*
  220. * push an element up or down to it's correct new location
  221. */
  222. static void
  223. fixHeap(int i, DBlock *b)
  224. {
  225. if(upHeap(i, b) == i)
  226. downHeap(i, b);
  227. }
  228. static int
  229. upHeap(int i, DBlock *b)
  230. {
  231. DBlock *bb;
  232. u32int now;
  233. int p;
  234. now = dCache.now;
  235. for(; i != 0; i = p){
  236. p = (i - 1) >> 1;
  237. bb = dCache.heap[p];
  238. if(b->used2 - now >= bb->used2 - now)
  239. break;
  240. dCache.heap[i] = bb;
  241. bb->heap = i;
  242. }
  243. dCache.heap[i] = b;
  244. b->heap = i;
  245. return i;
  246. }
  247. static int
  248. downHeap(int i, DBlock *b)
  249. {
  250. DBlock *bb;
  251. u32int now;
  252. int k;
  253. now = dCache.now;
  254. for(; ; i = k){
  255. k = (i << 1) + 1;
  256. if(k >= dCache.nheap)
  257. break;
  258. if(k + 1 < dCache.nheap && dCache.heap[k]->used2 - now > dCache.heap[k + 1]->used2 - now)
  259. k++;
  260. bb = dCache.heap[k];
  261. if(b->used2 - now <= bb->used2 - now)
  262. break;
  263. dCache.heap[i] = bb;
  264. bb->heap = i;
  265. }
  266. dCache.heap[i] = b;
  267. b->heap = i;
  268. return i;
  269. }
  270. static void
  271. findBlock(DBlock *bb)
  272. {
  273. DBlock *b, *last;
  274. int h;
  275. last = nil;
  276. h = pbHash(bb->addr);
  277. for(b = dCache.heads[h]; b != nil; b = b->next){
  278. if(last != b->prev)
  279. fatal("bad prev link");
  280. if(b == bb)
  281. return;
  282. last = b;
  283. }
  284. fatal("block missing from hash table");
  285. }
  286. void
  287. checkDCache(void)
  288. {
  289. DBlock *b;
  290. u32int size, now;
  291. int i, k, refed, nfree;
  292. vtLock(dCache.lock);
  293. size = dCache.size;
  294. now = dCache.now;
  295. for(i = 0; i < dCache.nheap; i++){
  296. if(dCache.heap[i]->heap != i)
  297. fatal("dc: mis-heaped at %d: %d", i, dCache.heap[i]->heap);
  298. if(i > 0 && dCache.heap[(i - 1) >> 1]->used2 - now > dCache.heap[i]->used2 - now)
  299. fatal("dc: bad heap ordering");
  300. k = (i << 1) + 1;
  301. if(k < dCache.nheap && dCache.heap[i]->used2 - now > dCache.heap[k]->used2 - now)
  302. fatal("dc: bad heap ordering");
  303. k++;
  304. if(k < dCache.nheap && dCache.heap[i]->used2 - now > dCache.heap[k]->used2 - now)
  305. fatal("dc: bad heap ordering");
  306. }
  307. refed = 0;
  308. for(i = 0; i < dCache.nblocks; i++){
  309. b = &dCache.blocks[i];
  310. if(b->data != &dCache.mem[i * size])
  311. fatal("dc: mis-blocked at %d", i);
  312. if(b->ref && b->heap == TWID32)
  313. refed++;
  314. if(b->addr)
  315. findBlock(b);
  316. if(b->heap != TWID32
  317. && dCache.heap[b->heap] != b)
  318. fatal("dc: spurious heap value");
  319. }
  320. nfree = 0;
  321. for(b = dCache.free; b != nil; b = b->next){
  322. if(b->addr != 0 || b->heap != TWID32)
  323. fatal("dc: bad free list");
  324. nfree++;
  325. }
  326. if(dCache.nheap + nfree + refed != dCache.nblocks)
  327. fatal("dc: missing blocks: %d %d %d", dCache.nheap, refed, dCache.nblocks);
  328. vtUnlock(dCache.lock);
  329. }