cache.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. /*
  10. * Memory-only VtBlock cache.
  11. *
  12. * The cached Venti blocks are in the hash chains.
  13. * The cached local blocks are only in the blocks array.
  14. * The free blocks are in the heap, which is supposed to
  15. * be indexed by second-to-last use but actually
  16. * appears to be last use.
  17. */
  18. #include <u.h>
  19. #include <libc.h>
  20. #include <venti.h>
  21. int vtcachenread;
  22. int vtcachencopy;
  23. int vtcachenwrite;
  24. int vttracelevel;
  25. enum {
  26. BioLocal = 1,
  27. BioVenti,
  28. BioReading,
  29. BioWriting,
  30. BioEmpty,
  31. BioVentiError
  32. };
  33. enum {
  34. BadHeap = ~0
  35. };
  36. struct VtCache
  37. {
  38. QLock lk;
  39. VtConn *z;
  40. uint32_t blocksize;
  41. uint32_t now; /* ticks for usage time stamps */
  42. VtBlock **hash; /* hash table for finding addresses */
  43. int nhash;
  44. VtBlock **heap; /* heap for finding victims */
  45. int nheap;
  46. VtBlock *block; /* all allocated blocks */
  47. int nblock;
  48. uint8_t *mem; /* memory for all blocks and data */
  49. int (*write)(VtConn*, uint8_t[VtScoreSize], uint,
  50. uint8_t*, int);
  51. };
  52. static void cachecheck(VtCache*);
  53. VtCache*
  54. vtcachealloc(VtConn *z, int blocksize, uint32_t nblock)
  55. {
  56. uint8_t *p;
  57. VtCache *c;
  58. int i;
  59. VtBlock *b;
  60. c = vtmallocz(sizeof(VtCache));
  61. c->z = z;
  62. c->blocksize = (blocksize + 127) & ~127;
  63. c->nblock = nblock;
  64. c->nhash = nblock;
  65. c->hash = vtmallocz(nblock*sizeof(VtBlock*));
  66. c->heap = vtmallocz(nblock*sizeof(VtBlock*));
  67. c->block = vtmallocz(nblock*sizeof(VtBlock));
  68. c->mem = vtmallocz(nblock*c->blocksize);
  69. c->write = vtwrite;
  70. p = c->mem;
  71. for(i=0; i<nblock; i++){
  72. b = &c->block[i];
  73. b->addr = NilBlock;
  74. b->c = c;
  75. b->data = p;
  76. b->heap = i;
  77. c->heap[i] = b;
  78. p += c->blocksize;
  79. }
  80. c->nheap = nblock;
  81. cachecheck(c);
  82. return c;
  83. }
  84. /*
  85. * BUG This is here so that vbackup can override it and do some
  86. * pipelining of writes. Arguably vtwrite or vtwritepacket or the
  87. * cache itself should be providing this functionality.
  88. */
  89. void
  90. vtcachesetwrite(VtCache *c,
  91. int (*write)(VtConn*, uint8_t[VtScoreSize], uint, uint8_t*, int))
  92. {
  93. if(write == nil)
  94. write = vtwrite;
  95. c->write = write;
  96. }
  97. void
  98. vtcachefree(VtCache *c)
  99. {
  100. int i;
  101. qlock(&c->lk);
  102. cachecheck(c);
  103. for(i=0; i<c->nblock; i++)
  104. assert(c->block[i].ref == 0);
  105. vtfree(c->hash);
  106. vtfree(c->heap);
  107. vtfree(c->block);
  108. vtfree(c->mem);
  109. vtfree(c);
  110. }
  111. static void
  112. vtcachedump(VtCache *c)
  113. {
  114. int i;
  115. VtBlock *b;
  116. for(i=0; i<c->nblock; i++){
  117. b = &c->block[i];
  118. print("cache block %d: type %d score %V iostate %d addr %d ref %d nlock %d\n",
  119. i, b->type, b->score, b->iostate, b->addr, b->ref, b->nlock);
  120. }
  121. }
  122. static void
  123. cachecheck(VtCache *c)
  124. {
  125. uint32_t size, now;
  126. int i, k, refed;
  127. VtBlock *b;
  128. size = c->blocksize;
  129. now = c->now;
  130. for(i = 0; i < c->nheap; i++){
  131. if(c->heap[i]->heap != i)
  132. sysfatal("mis-heaped at %d: %d", i, c->heap[i]->heap);
  133. if(i > 0 && c->heap[(i - 1) >> 1]->used - now > c->heap[i]->used - now)
  134. sysfatal("bad heap ordering");
  135. k = (i << 1) + 1;
  136. if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now)
  137. sysfatal("bad heap ordering");
  138. k++;
  139. if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now)
  140. sysfatal("bad heap ordering");
  141. }
  142. refed = 0;
  143. for(i = 0; i < c->nblock; i++){
  144. b = &c->block[i];
  145. if(b->data != &c->mem[i * size])
  146. sysfatal("mis-blocked at %d", i);
  147. if(b->ref && b->heap == BadHeap)
  148. refed++;
  149. else if(b->addr != NilBlock)
  150. refed++;
  151. }
  152. assert(c->nheap + refed == c->nblock);
  153. refed = 0;
  154. for(i = 0; i < c->nblock; i++){
  155. b = &c->block[i];
  156. if(b->ref){
  157. refed++;
  158. }
  159. }
  160. }
  161. static int
  162. upheap(int i, VtBlock *b)
  163. {
  164. VtBlock *bb;
  165. uint32_t now;
  166. int p;
  167. VtCache *c;
  168. c = b->c;
  169. now = c->now;
  170. for(; i != 0; i = p){
  171. p = (i - 1) >> 1;
  172. bb = c->heap[p];
  173. if(b->used - now >= bb->used - now)
  174. break;
  175. c->heap[i] = bb;
  176. bb->heap = i;
  177. }
  178. c->heap[i] = b;
  179. b->heap = i;
  180. return i;
  181. }
  182. static int
  183. downheap(int i, VtBlock *b)
  184. {
  185. VtBlock *bb;
  186. uint32_t now;
  187. int k;
  188. VtCache *c;
  189. c = b->c;
  190. now = c->now;
  191. for(; ; i = k){
  192. k = (i << 1) + 1;
  193. if(k >= c->nheap)
  194. break;
  195. if(k + 1 < c->nheap && c->heap[k]->used - now > c->heap[k + 1]->used - now)
  196. k++;
  197. bb = c->heap[k];
  198. if(b->used - now <= bb->used - now)
  199. break;
  200. c->heap[i] = bb;
  201. bb->heap = i;
  202. }
  203. c->heap[i] = b;
  204. b->heap = i;
  205. return i;
  206. }
  207. /*
  208. * Delete a block from the heap.
  209. * Called with c->lk held.
  210. */
  211. static void
  212. heapdel(VtBlock *b)
  213. {
  214. int i, si;
  215. VtCache *c;
  216. c = b->c;
  217. si = b->heap;
  218. if(si == BadHeap)
  219. return;
  220. b->heap = BadHeap;
  221. c->nheap--;
  222. if(si == c->nheap)
  223. return;
  224. b = c->heap[c->nheap];
  225. i = upheap(si, b);
  226. if(i == si)
  227. downheap(i, b);
  228. }
  229. /*
  230. * Insert a block into the heap.
  231. * Called with c->lk held.
  232. */
  233. static void
  234. heapins(VtBlock *b)
  235. {
  236. assert(b->heap == BadHeap);
  237. upheap(b->c->nheap++, b);
  238. }
  239. /*
  240. * locate the vtBlock with the oldest second to last use.
  241. * remove it from the heap, and fix up the heap.
  242. */
  243. /* called with c->lk held */
  244. static VtBlock*
  245. vtcachebumpblock(VtCache *c)
  246. {
  247. VtBlock *b;
  248. /*
  249. * locate the vtBlock with the oldest second to last use.
  250. * remove it from the heap, and fix up the heap.
  251. */
  252. if(c->nheap == 0){
  253. vtcachedump(c);
  254. fprint(2, "vtcachebumpblock: no free blocks in vtCache");
  255. abort();
  256. }
  257. b = c->heap[0];
  258. heapdel(b);
  259. assert(b->heap == BadHeap);
  260. assert(b->ref == 0);
  261. /*
  262. * unchain the vtBlock from hash chain if any
  263. */
  264. if(b->prev){
  265. *(b->prev) = b->next;
  266. if(b->next)
  267. b->next->prev = b->prev;
  268. b->prev = nil;
  269. }
  270. if(0)fprint(2, "droping %x:%V\n", b->addr, b->score);
  271. /* set vtBlock to a reasonable state */
  272. b->ref = 1;
  273. b->iostate = BioEmpty;
  274. return b;
  275. }
  276. /*
  277. * fetch a local block from the memory cache.
  278. * if it's not there, load it, bumping some other Block.
  279. * if we're out of free blocks, we're screwed.
  280. */
  281. VtBlock*
  282. vtcachelocal(VtCache *c, uint32_t addr, int type)
  283. {
  284. VtBlock *b;
  285. if(addr == 0)
  286. sysfatal("vtcachelocal: asked for nonexistent block 0");
  287. if(addr > c->nblock)
  288. sysfatal("vtcachelocal: asked for block #%u; only %d blocks",
  289. addr, c->nblock);
  290. b = &c->block[addr-1];
  291. if(b->addr == NilBlock || b->iostate != BioLocal)
  292. sysfatal("vtcachelocal: block is not local");
  293. if(b->type != type)
  294. sysfatal("vtcachelocal: block has wrong type %d != %d", b->type, type);
  295. qlock(&c->lk);
  296. b->ref++;
  297. qunlock(&c->lk);
  298. qlock(&b->lk);
  299. b->nlock = 1;
  300. b->pc = getcallerpc();
  301. return b;
  302. }
  303. VtBlock*
  304. vtcacheallocblock(VtCache *c, int type)
  305. {
  306. VtBlock *b;
  307. qlock(&c->lk);
  308. b = vtcachebumpblock(c);
  309. b->iostate = BioLocal;
  310. b->type = type;
  311. b->addr = (b - c->block)+1;
  312. vtzeroextend(type, b->data, 0, c->blocksize);
  313. vtlocaltoglobal(b->addr, b->score);
  314. qunlock(&c->lk);
  315. qlock(&b->lk);
  316. b->nlock = 1;
  317. b->pc = getcallerpc();
  318. return b;
  319. }
  320. /*
  321. * fetch a global (Venti) block from the memory cache.
  322. * if it's not there, load it, bumping some other block.
  323. */
  324. VtBlock*
  325. vtcacheglobal(VtCache *c, uint8_t score[VtScoreSize], int type)
  326. {
  327. VtBlock *b;
  328. uint32_t h;
  329. int n;
  330. uint32_t addr;
  331. if(vttracelevel)
  332. fprint(2, "vtcacheglobal %V %d from %p\n", score, type, getcallerpc());
  333. addr = vtglobaltolocal(score);
  334. if(addr != NilBlock){
  335. if(vttracelevel)
  336. fprint(2, "vtcacheglobal %V %d => local\n", score, type);
  337. b = vtcachelocal(c, addr, type);
  338. if(b)
  339. b->pc = getcallerpc();
  340. return b;
  341. }
  342. h = (uint32_t)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c->nhash;
  343. /*
  344. * look for the block in the cache
  345. */
  346. qlock(&c->lk);
  347. for(b = c->hash[h]; b != nil; b = b->next){
  348. if(b->addr != NilBlock || memcmp(b->score, score, VtScoreSize) != 0 || b->type != type)
  349. continue;
  350. heapdel(b);
  351. b->ref++;
  352. qunlock(&c->lk);
  353. if(vttracelevel)
  354. fprint(2, "vtcacheglobal %V %d => found in cache %p; locking\n", score, type, b);
  355. qlock(&b->lk);
  356. b->nlock = 1;
  357. if(b->iostate == BioVentiError){
  358. if(chattyventi)
  359. fprint(2, "cached read error for %V\n", score);
  360. if(vttracelevel)
  361. fprint(2, "vtcacheglobal %V %d => cache read error\n", score, type);
  362. vtblockput(b);
  363. werrstr("venti i/o error");
  364. return nil;
  365. }
  366. if(vttracelevel)
  367. fprint(2, "vtcacheglobal %V %d => found in cache; returning\n", score, type);
  368. b->pc = getcallerpc();
  369. return b;
  370. }
  371. /*
  372. * not found
  373. */
  374. b = vtcachebumpblock(c);
  375. b->addr = NilBlock;
  376. b->type = type;
  377. memmove(b->score, score, VtScoreSize);
  378. /* chain onto correct hash */
  379. b->next = c->hash[h];
  380. c->hash[h] = b;
  381. if(b->next != nil)
  382. b->next->prev = &b->next;
  383. b->prev = &c->hash[h];
  384. /*
  385. * Lock b before unlocking c, so that others wait while we read.
  386. *
  387. * You might think there is a race between this qlock(b) before qunlock(c)
  388. * and the qlock(c) while holding a qlock(b) in vtblockwrite. However,
  389. * the block here can never be the block in a vtblockwrite, so we're safe.
  390. * We're certainly living on the edge.
  391. */
  392. if(vttracelevel)
  393. fprint(2, "vtcacheglobal %V %d => bumped; locking %p\n", score, type, b);
  394. qlock(&b->lk);
  395. b->nlock = 1;
  396. qunlock(&c->lk);
  397. vtcachenread++;
  398. n = vtread(c->z, score, type, b->data, c->blocksize);
  399. if(n < 0){
  400. if(chattyventi)
  401. fprint(2, "read %V: %r\n", score);
  402. if(vttracelevel)
  403. fprint(2, "vtcacheglobal %V %d => bumped; read error\n", score, type);
  404. b->iostate = BioVentiError;
  405. vtblockput(b);
  406. return nil;
  407. }
  408. vtzeroextend(type, b->data, n, c->blocksize);
  409. b->iostate = BioVenti;
  410. b->nlock = 1;
  411. if(vttracelevel)
  412. fprint(2, "vtcacheglobal %V %d => loaded into cache; returning\n", score, type);
  413. b->pc = getcallerpc();
  414. return b;
  415. }
  416. /*
  417. * The thread that has locked b may refer to it by
  418. * multiple names. Nlock counts the number of
  419. * references the locking thread holds. It will call
  420. * vtblockput once per reference.
  421. */
  422. void
  423. vtblockduplock(VtBlock *b)
  424. {
  425. assert(b->nlock > 0);
  426. b->nlock++;
  427. }
  428. /*
  429. * we're done with the block.
  430. * unlock it. can't use it after calling this.
  431. */
  432. void
  433. vtblockput(VtBlock* b)
  434. {
  435. VtCache *c;
  436. if(b == nil)
  437. return;
  438. if(0)fprint(2, "vtblockput: %d: %x %d %d\n", getpid(), b->addr, c->nheap, b->iostate);
  439. if(vttracelevel)
  440. fprint(2, "vtblockput %p from %p\n", b, getcallerpc());
  441. if(--b->nlock > 0)
  442. return;
  443. /*
  444. * b->nlock should probably stay at zero while
  445. * the vtBlock is unlocked, but diskThread and vtSleep
  446. * conspire to assume that they can just qlock(&b->lk); vtblockput(b),
  447. * so we have to keep b->nlock set to 1 even
  448. * when the vtBlock is unlocked.
  449. */
  450. assert(b->nlock == 0);
  451. b->nlock = 1;
  452. qunlock(&b->lk);
  453. c = b->c;
  454. qlock(&c->lk);
  455. if(--b->ref > 0){
  456. qunlock(&c->lk);
  457. return;
  458. }
  459. assert(b->ref == 0);
  460. switch(b->iostate){
  461. case BioVenti:
  462. /*if(b->addr != NilBlock) print("blockput %d\n", b->addr); */
  463. b->used = c->now++;
  464. /* fall through */
  465. case BioVentiError:
  466. heapins(b);
  467. break;
  468. case BioLocal:
  469. break;
  470. }
  471. qunlock(&c->lk);
  472. }
  473. int
  474. vtblockwrite(VtBlock *b)
  475. {
  476. uint8_t score[VtScoreSize];
  477. VtCache *c;
  478. uint h;
  479. int n;
  480. if(b->iostate != BioLocal){
  481. werrstr("vtblockwrite: not a local block");
  482. return -1;
  483. }
  484. c = b->c;
  485. n = vtzerotruncate(b->type, b->data, c->blocksize);
  486. vtcachenwrite++;
  487. if(c->write(c->z, score, b->type, b->data, n) < 0)
  488. return -1;
  489. memmove(b->score, score, VtScoreSize);
  490. qlock(&c->lk);
  491. b->addr = NilBlock; /* now on venti */
  492. b->iostate = BioVenti;
  493. h = (uint32_t)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c->nhash;
  494. b->next = c->hash[h];
  495. c->hash[h] = b;
  496. if(b->next != nil)
  497. b->next->prev = &b->next;
  498. b->prev = &c->hash[h];
  499. qunlock(&c->lk);
  500. return 0;
  501. }
  502. uint
  503. vtcacheblocksize(VtCache *c)
  504. {
  505. return c->blocksize;
  506. }
  507. VtBlock*
  508. vtblockcopy(VtBlock *b)
  509. {
  510. VtBlock *bb;
  511. vtcachencopy++;
  512. bb = vtcacheallocblock(b->c, b->type);
  513. if(bb == nil){
  514. vtblockput(b);
  515. return nil;
  516. }
  517. memmove(bb->data, b->data, b->c->blocksize);
  518. vtblockput(b);
  519. bb->pc = getcallerpc();
  520. return bb;
  521. }
  522. void
  523. vtlocaltoglobal(uint32_t addr, uint8_t score[VtScoreSize])
  524. {
  525. memset(score, 0, 16);
  526. score[16] = addr>>24;
  527. score[17] = addr>>16;
  528. score[18] = addr>>8;
  529. score[19] = addr;
  530. }
  531. uint32_t
  532. vtglobaltolocal(uint8_t score[VtScoreSize])
  533. {
  534. static uint8_t zero[16];
  535. if(memcmp(score, zero, 16) != 0)
  536. return NilBlock;
  537. return (score[16]<<24)|(score[17]<<16)|(score[18]<<8)|score[19];
  538. }