cache.c 12 KB

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