icache.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. #include "stdinc.h"
  2. #include "dat.h"
  3. #include "fns.h"
  4. int icacheprefetch = 1;
  5. typedef struct ICache ICache;
  6. typedef struct IHash IHash;
  7. typedef struct ISum ISum;
  8. struct ICache
  9. {
  10. QLock lock;
  11. Rendez full;
  12. IHash *hash;
  13. IEntry *entries;
  14. int nentries;
  15. IEntry free;
  16. IEntry clean;
  17. IEntry dirty;
  18. u32int maxdirty;
  19. u32int ndirty;
  20. AState as;
  21. ISum **sum;
  22. int nsum;
  23. IHash *shash;
  24. IEntry *sentries;
  25. int nsentries;
  26. };
  27. static ICache icache;
  28. /*
  29. * Hash table of IEntries
  30. */
  31. struct IHash
  32. {
  33. int bits;
  34. u32int size;
  35. IEntry **table;
  36. };
  37. static IHash*
  38. mkihash(int size1)
  39. {
  40. u32int size;
  41. int bits;
  42. IHash *ih;
  43. bits = 0;
  44. size = 1;
  45. while(size < size1){
  46. bits++;
  47. size <<= 1;
  48. }
  49. ih = vtmallocz(sizeof(IHash)+size*sizeof(ih->table[0]));
  50. ih->table = (IEntry**)(ih+1);
  51. ih->bits = bits;
  52. ih->size = size;
  53. return ih;
  54. }
  55. static IEntry*
  56. ihashlookup(IHash *ih, u8int score[VtScoreSize], int type)
  57. {
  58. u32int h;
  59. IEntry *ie;
  60. h = hashbits(score, ih->bits);
  61. for(ie=ih->table[h]; ie; ie=ie->nexthash)
  62. if((type == -1 || type == ie->ia.type) && scorecmp(score, ie->score) == 0)
  63. return ie;
  64. return nil;
  65. }
  66. static void
  67. ihashdelete(IHash *ih, IEntry *ie, char *what)
  68. {
  69. u32int h;
  70. IEntry **l;
  71. h = hashbits(ie->score, ih->bits);
  72. for(l=&ih->table[h]; *l; l=&(*l)->nexthash)
  73. if(*l == ie){
  74. *l = ie->nexthash;
  75. return;
  76. }
  77. fprint(2, "warning: %s %V not found in ihashdelete\n", what, ie->score);
  78. }
  79. static void
  80. ihashinsert(IHash *ih, IEntry *ie)
  81. {
  82. u32int h;
  83. h = hashbits(ie->score, ih->bits);
  84. ie->nexthash = ih->table[h];
  85. ih->table[h] = ie;
  86. }
  87. /*
  88. * IEntry lists.
  89. */
  90. static IEntry*
  91. popout(IEntry *ie)
  92. {
  93. if(ie->prev == nil && ie->next == nil)
  94. return ie;
  95. ie->prev->next = ie->next;
  96. ie->next->prev = ie->prev;
  97. ie->next = nil;
  98. ie->prev = nil;
  99. return ie;
  100. }
  101. static IEntry*
  102. poplast(IEntry *list)
  103. {
  104. if(list->prev == list)
  105. return nil;
  106. return popout(list->prev);
  107. }
  108. static IEntry*
  109. pushfirst(IEntry *list, IEntry *ie)
  110. {
  111. popout(ie);
  112. ie->prev = list;
  113. ie->next = list->next;
  114. ie->prev->next = ie;
  115. ie->next->prev = ie;
  116. return ie;
  117. }
  118. /*
  119. * Arena summary cache.
  120. */
  121. struct ISum
  122. {
  123. QLock lock;
  124. IEntry *entries;
  125. int nentries;
  126. int loaded;
  127. u64int addr;
  128. u64int limit;
  129. Arena *arena;
  130. int g;
  131. };
  132. static ISum*
  133. scachelookup(u64int addr)
  134. {
  135. int i;
  136. ISum *s;
  137. for(i=0; i<icache.nsum; i++){
  138. s = icache.sum[i];
  139. if(s->addr <= addr && addr < s->limit){
  140. if(i > 0){
  141. memmove(icache.sum+1, icache.sum, i*sizeof icache.sum[0]);
  142. icache.sum[0] = s;
  143. }
  144. return s;
  145. }
  146. }
  147. return nil;
  148. }
  149. static void
  150. sumclear(ISum *s)
  151. {
  152. int i;
  153. for(i=0; i<s->nentries; i++)
  154. ihashdelete(icache.shash, &s->entries[i], "scache");
  155. s->nentries = 0;
  156. s->loaded = 0;
  157. s->addr = 0;
  158. s->limit = 0;
  159. s->arena = nil;
  160. s->g = 0;
  161. }
  162. static ISum*
  163. scacheevict(void)
  164. {
  165. ISum *s;
  166. int i;
  167. for(i=icache.nsum-1; i>=0; i--){
  168. s = icache.sum[i];
  169. if(canqlock(&s->lock)){
  170. if(i > 0){
  171. memmove(icache.sum+1, icache.sum, i*sizeof icache.sum[0]);
  172. icache.sum[0] = s;
  173. }
  174. sumclear(s);
  175. return s;
  176. }
  177. }
  178. return nil;
  179. }
  180. static void
  181. scachehit(u64int addr)
  182. {
  183. scachelookup(addr); /* for move-to-front */
  184. }
  185. static void
  186. scachesetup(ISum *s, u64int addr)
  187. {
  188. u64int addr0, limit;
  189. int g;
  190. s->arena = amapitoag(mainindex, addr, &addr0, &limit, &g);
  191. s->addr = addr0;
  192. s->limit = limit;
  193. s->g = g;
  194. }
  195. static void
  196. scacheload(ISum *s)
  197. {
  198. int i, n;
  199. s->loaded = 1;
  200. n = asumload(s->arena, s->g, s->entries, ArenaCIGSize);
  201. /*
  202. * n can be less then ArenaCIGSize, either if the clump group
  203. * is the last in the arena and is only partially filled, or if there
  204. * are corrupt clumps in the group -- those are not returned.
  205. */
  206. for(i=0; i<n; i++){
  207. s->entries[i].ia.addr += s->addr;
  208. ihashinsert(icache.shash, &s->entries[i]);
  209. }
  210. //fprint(2, "%T scacheload %s %d - %d entries\n", s->arena->name, s->g, n);
  211. addstat(StatScachePrefetch, n);
  212. s->nentries = n;
  213. }
  214. static ISum*
  215. scachemiss(u64int addr)
  216. {
  217. ISum *s;
  218. if(!icacheprefetch)
  219. return nil;
  220. s = scachelookup(addr);
  221. if(s == nil){
  222. /* first time: make an entry in the cache but don't populate it yet */
  223. s = scacheevict();
  224. if(s == nil)
  225. return nil;
  226. scachesetup(s, addr);
  227. qunlock(&s->lock);
  228. return nil;
  229. }
  230. /* second time: load from disk */
  231. qlock(&s->lock);
  232. if(s->loaded || !icacheprefetch){
  233. qunlock(&s->lock);
  234. return nil;
  235. }
  236. return s; /* locked */
  237. }
  238. /*
  239. * Index cache.
  240. */
  241. void
  242. initicache(u32int mem0)
  243. {
  244. u32int mem;
  245. int i, entries, scache;
  246. icache.full.l = &icache.lock;
  247. mem = mem0;
  248. entries = mem / (sizeof(IEntry)+sizeof(IEntry*));
  249. scache = (entries/8) / ArenaCIGSize;
  250. entries -= entries/8;
  251. if(scache < 4)
  252. scache = 4;
  253. if(scache > 16)
  254. scache = 16;
  255. if(entries < 1000)
  256. entries = 1000;
  257. fprint(2, "icache %,d bytes = %,d entries; %d scache\n", mem0, entries, scache);
  258. icache.clean.prev = icache.clean.next = &icache.clean;
  259. icache.dirty.prev = icache.dirty.next = &icache.dirty;
  260. icache.free.prev = icache.free.next = &icache.free;
  261. icache.hash = mkihash(entries);
  262. icache.nentries = entries;
  263. setstat(StatIcacheSize, entries);
  264. icache.entries = vtmallocz(entries*sizeof icache.entries[0]);
  265. icache.maxdirty = entries / 2;
  266. for(i=0; i<entries; i++)
  267. pushfirst(&icache.free, &icache.entries[i]);
  268. icache.nsum = scache;
  269. icache.sum = vtmallocz(scache*sizeof icache.sum[0]);
  270. icache.sum[0] = vtmallocz(scache*sizeof icache.sum[0][0]);
  271. icache.nsentries = scache * ArenaCIGSize;
  272. icache.sentries = vtmallocz(scache*ArenaCIGSize*sizeof icache.sentries[0]);
  273. icache.shash = mkihash(scache*ArenaCIGSize);
  274. for(i=0; i<scache; i++){
  275. icache.sum[i] = icache.sum[0] + i;
  276. icache.sum[i]->entries = icache.sentries + i*ArenaCIGSize;
  277. }
  278. }
  279. static IEntry*
  280. evictlru(void)
  281. {
  282. IEntry *ie;
  283. ie = poplast(&icache.clean);
  284. if(ie == nil)
  285. return nil;
  286. ihashdelete(icache.hash, ie, "evictlru");
  287. return ie;
  288. }
  289. static void
  290. icacheinsert(u8int score[VtScoreSize], IAddr *ia, int state)
  291. {
  292. IEntry *ie;
  293. if((ie = poplast(&icache.free)) == nil && (ie = evictlru()) == nil){
  294. addstat(StatIcacheStall, 1);
  295. while((ie = poplast(&icache.free)) == nil && (ie = evictlru()) == nil){
  296. // Could safely return here if state == IEClean.
  297. // But if state == IEDirty, have to wait to make
  298. // sure we don't lose an index write.
  299. // Let's wait all the time.
  300. flushdcache();
  301. kickicache();
  302. rsleep(&icache.full);
  303. }
  304. addstat(StatIcacheStall, -1);
  305. }
  306. memmove(ie->score, score, VtScoreSize);
  307. ie->state = state;
  308. ie->ia = *ia;
  309. if(state == IEClean){
  310. addstat(StatIcachePrefetch, 1);
  311. pushfirst(&icache.clean, ie);
  312. }else{
  313. addstat(StatIcacheWrite, 1);
  314. assert(state == IEDirty);
  315. icache.ndirty++;
  316. setstat(StatIcacheDirty, icache.ndirty);
  317. delaykickicache();
  318. pushfirst(&icache.dirty, ie);
  319. }
  320. ihashinsert(icache.hash, ie);
  321. }
  322. int
  323. icachelookup(u8int score[VtScoreSize], int type, IAddr *ia)
  324. {
  325. IEntry *ie;
  326. qlock(&icache.lock);
  327. addstat(StatIcacheLookup, 1);
  328. if((ie = ihashlookup(icache.hash, score, type)) != nil){
  329. *ia = ie->ia;
  330. if(ie->state == IEClean)
  331. pushfirst(&icache.clean, ie);
  332. addstat(StatIcacheHit, 1);
  333. qunlock(&icache.lock);
  334. return 0;
  335. }
  336. if((ie = ihashlookup(icache.shash, score, type)) != nil){
  337. *ia = ie->ia;
  338. icacheinsert(score, &ie->ia, IEClean);
  339. scachehit(ie->ia.addr);
  340. addstat(StatScacheHit, 1);
  341. qunlock(&icache.lock);
  342. return 0;
  343. }
  344. addstat(StatIcacheMiss, 1);
  345. qunlock(&icache.lock);
  346. return -1;
  347. }
  348. int
  349. insertscore(u8int score[VtScoreSize], IAddr *ia, int state, AState *as)
  350. {
  351. ISum *toload;
  352. qlock(&icache.lock);
  353. icacheinsert(score, ia, state);
  354. if(state == IEClean)
  355. toload = scachemiss(ia->addr);
  356. else{
  357. assert(state == IEDirty);
  358. toload = nil;
  359. if(as == nil)
  360. fprint(2, "%T insertscore IEDirty without as; called from %#p\n",
  361. getcallerpc(&score));
  362. else{
  363. if(icache.as.aa > as->aa)
  364. fprint(2, "%T insertscore: aa moving backward: %#llux -> %#llux\n", icache.as.aa, as->aa);
  365. icache.as = *as;
  366. }
  367. }
  368. qunlock(&icache.lock);
  369. if(toload){
  370. scacheload(toload);
  371. qunlock(&toload->lock);
  372. }
  373. if(icache.ndirty >= icache.maxdirty)
  374. kickicache();
  375. /*
  376. * It's okay not to do this under icache.lock.
  377. * Calling insertscore only happens when we hold
  378. * the lump, meaning any searches for this block
  379. * will hit in the lump cache until after we return.
  380. */
  381. if(state == IEDirty)
  382. markbloomfilter(mainindex->bloom, score);
  383. return 0;
  384. }
  385. int
  386. lookupscore(u8int score[VtScoreSize], int type, IAddr *ia)
  387. {
  388. int ms, ret;
  389. IEntry d;
  390. if(icachelookup(score, type, ia) >= 0){
  391. addstat(StatIcacheRead, 1);
  392. return 0;
  393. }
  394. ms = msec();
  395. addstat(StatIcacheFill, 1);
  396. if(loadientry(mainindex, score, type, &d) < 0)
  397. ret = -1;
  398. else{
  399. ret = 0;
  400. insertscore(score, &d.ia, IEClean, nil);
  401. *ia = d.ia;
  402. }
  403. addstat2(StatIcacheRead, 1, StatIcacheReadTime, msec() - ms);
  404. return ret;
  405. }
  406. u32int
  407. hashbits(u8int *sc, int bits)
  408. {
  409. u32int v;
  410. v = (sc[0] << 24) | (sc[1] << 16) | (sc[2] << 8) | sc[3];
  411. if(bits < 32)
  412. v >>= (32 - bits);
  413. return v;
  414. }
  415. ulong
  416. icachedirtyfrac(void)
  417. {
  418. return (vlong)icache.ndirty*IcacheFrac / icache.nentries;
  419. }
  420. /*
  421. * Return a singly-linked list of dirty index entries.
  422. * with 32-bit hash numbers between lo and hi
  423. * and address < limit.
  424. */
  425. IEntry*
  426. icachedirty(u32int lo, u32int hi, u64int limit)
  427. {
  428. u32int h;
  429. IEntry *ie, *dirty;
  430. dirty = nil;
  431. trace(TraceProc, "icachedirty enter");
  432. qlock(&icache.lock);
  433. for(ie = icache.dirty.next; ie != &icache.dirty; ie=ie->next){
  434. if(ie->state == IEDirty && ie->ia.addr <= limit){
  435. h = hashbits(ie->score, 32);
  436. if(lo <= h && h <= hi){
  437. ie->nextdirty = dirty;
  438. dirty = ie;
  439. }
  440. }
  441. }
  442. qunlock(&icache.lock);
  443. trace(TraceProc, "icachedirty exit");
  444. if(dirty == nil)
  445. flushdcache();
  446. return dirty;
  447. }
  448. AState
  449. icachestate(void)
  450. {
  451. AState as;
  452. qlock(&icache.lock);
  453. as = icache.as;
  454. qunlock(&icache.lock);
  455. return as;
  456. }
  457. /*
  458. * The singly-linked non-circular list of index entries ie
  459. * has been written to disk. Move them to the clean list.
  460. */
  461. void
  462. icacheclean(IEntry *ie)
  463. {
  464. IEntry *next;
  465. trace(TraceProc, "icacheclean enter");
  466. qlock(&icache.lock);
  467. for(; ie; ie=next){
  468. assert(ie->state == IEDirty);
  469. next = ie->nextdirty;
  470. ie->nextdirty = nil;
  471. popout(ie); /* from icache.dirty */
  472. icache.ndirty--;
  473. ie->state = IEClean;
  474. pushfirst(&icache.clean, ie);
  475. }
  476. setstat(StatIcacheDirty, icache.ndirty);
  477. rwakeupall(&icache.full);
  478. qunlock(&icache.lock);
  479. trace(TraceProc, "icacheclean exit");
  480. }
  481. void
  482. emptyicache(void)
  483. {
  484. int i;
  485. IEntry *ie;
  486. ISum *s;
  487. qlock(&icache.lock);
  488. while((ie = evictlru()) != nil)
  489. pushfirst(&icache.free, ie);
  490. for(i=0; i<icache.nsum; i++){
  491. s = icache.sum[i];
  492. qlock(&s->lock);
  493. sumclear(s);
  494. qunlock(&s->lock);
  495. }
  496. qunlock(&icache.lock);
  497. }