icache.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  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. s = scachelookup(addr);
  219. if(s == nil){
  220. /* first time: make an entry in the cache but don't populate it yet */
  221. s = scacheevict();
  222. if(s == nil)
  223. return nil;
  224. scachesetup(s, addr);
  225. qunlock(&s->lock);
  226. return nil;
  227. }
  228. /* second time: load from disk */
  229. qlock(&s->lock);
  230. if(s->loaded || !icacheprefetch){
  231. qunlock(&s->lock);
  232. return nil;
  233. }
  234. return s; /* locked */
  235. }
  236. /*
  237. * Index cache.
  238. */
  239. void
  240. initicache(u32int mem0)
  241. {
  242. u32int mem;
  243. int i, entries, scache;
  244. icache.full.l = &icache.lock;
  245. mem = mem0;
  246. entries = mem / (sizeof(IEntry)+sizeof(IEntry*));
  247. scache = (entries/8) / ArenaCIGSize;
  248. entries -= entries/8;
  249. if(scache < 4)
  250. scache = 4;
  251. if(scache > 16)
  252. scache = 16;
  253. if(entries < 1000)
  254. entries = 1000;
  255. fprint(2, "icache %,d bytes = %,d entries; %d scache\n", mem0, entries, scache);
  256. icache.clean.prev = icache.clean.next = &icache.clean;
  257. icache.dirty.prev = icache.dirty.next = &icache.dirty;
  258. icache.free.prev = icache.free.next = &icache.free;
  259. icache.hash = mkihash(entries);
  260. icache.nentries = entries;
  261. setstat(StatIcacheSize, entries);
  262. icache.entries = vtmallocz(entries*sizeof icache.entries[0]);
  263. icache.maxdirty = entries / 2;
  264. for(i=0; i<entries; i++)
  265. pushfirst(&icache.free, &icache.entries[i]);
  266. icache.nsum = scache;
  267. icache.sum = vtmallocz(scache*sizeof icache.sum[0]);
  268. icache.sum[0] = vtmallocz(scache*sizeof icache.sum[0][0]);
  269. icache.nsentries = scache * ArenaCIGSize;
  270. icache.sentries = vtmallocz(scache*ArenaCIGSize*sizeof icache.sentries[0]);
  271. icache.shash = mkihash(scache*ArenaCIGSize);
  272. for(i=0; i<scache; i++){
  273. icache.sum[i] = icache.sum[0] + i;
  274. icache.sum[i]->entries = icache.sentries + i*ArenaCIGSize;
  275. }
  276. }
  277. static IEntry*
  278. evictlru(void)
  279. {
  280. IEntry *ie;
  281. ie = poplast(&icache.clean);
  282. if(ie == nil)
  283. return nil;
  284. ihashdelete(icache.hash, ie, "evictlru");
  285. return ie;
  286. }
  287. static void
  288. icacheinsert(u8int score[VtScoreSize], IAddr *ia, int state)
  289. {
  290. IEntry *ie;
  291. if((ie = poplast(&icache.free)) == nil && (ie = evictlru()) == nil){
  292. addstat(StatIcacheStall, 1);
  293. while((ie = poplast(&icache.free)) == nil && (ie = evictlru()) == nil){
  294. // Could safely return here if state == IEClean.
  295. // But if state == IEDirty, have to wait to make
  296. // sure we don't lose an index write.
  297. // Let's wait all the time.
  298. flushdcache();
  299. kickicache();
  300. rsleep(&icache.full);
  301. }
  302. addstat(StatIcacheStall, -1);
  303. }
  304. memmove(ie->score, score, VtScoreSize);
  305. ie->state = state;
  306. ie->ia = *ia;
  307. if(state == IEClean){
  308. addstat(StatIcachePrefetch, 1);
  309. pushfirst(&icache.clean, ie);
  310. }else{
  311. addstat(StatIcacheWrite, 1);
  312. assert(state == IEDirty);
  313. icache.ndirty++;
  314. setstat(StatIcacheDirty, icache.ndirty);
  315. delaykickicache();
  316. pushfirst(&icache.dirty, ie);
  317. }
  318. ihashinsert(icache.hash, ie);
  319. }
  320. int
  321. icachelookup(u8int score[VtScoreSize], int type, IAddr *ia)
  322. {
  323. IEntry *ie;
  324. qlock(&icache.lock);
  325. addstat(StatIcacheLookup, 1);
  326. if((ie = ihashlookup(icache.hash, score, type)) != nil){
  327. *ia = ie->ia;
  328. if(ie->state == IEClean)
  329. pushfirst(&icache.clean, ie);
  330. addstat(StatIcacheHit, 1);
  331. qunlock(&icache.lock);
  332. return 0;
  333. }
  334. if((ie = ihashlookup(icache.shash, score, type)) != nil){
  335. *ia = ie->ia;
  336. icacheinsert(score, &ie->ia, IEClean);
  337. scachehit(ie->ia.addr);
  338. addstat(StatScacheHit, 1);
  339. qunlock(&icache.lock);
  340. return 0;
  341. }
  342. addstat(StatIcacheMiss, 1);
  343. qunlock(&icache.lock);
  344. return -1;
  345. }
  346. int
  347. insertscore(u8int score[VtScoreSize], IAddr *ia, int state, AState *as)
  348. {
  349. ISum *toload;
  350. qlock(&icache.lock);
  351. icacheinsert(score, ia, state);
  352. if(state == IEClean)
  353. toload = scachemiss(ia->addr);
  354. else{
  355. assert(state == IEDirty);
  356. toload = nil;
  357. if(as == nil)
  358. fprint(2, "%T insertscore IEDirty without as; called from %lux\n", getcallerpc(&score));
  359. else{
  360. if(icache.as.aa > as->aa)
  361. fprint(2, "%T insertscore: aa moving backward: %#llux -> %#llux\n", icache.as.aa, as->aa);
  362. icache.as = *as;
  363. }
  364. }
  365. qunlock(&icache.lock);
  366. if(toload){
  367. scacheload(toload);
  368. qunlock(&toload->lock);
  369. }
  370. if(icache.ndirty >= icache.maxdirty)
  371. kickicache();
  372. /*
  373. * It's okay not to do this under icache.lock.
  374. * Calling insertscore only happens when we hold
  375. * the lump, meaning any searches for this block
  376. * will hit in the lump cache until after we return.
  377. */
  378. if(state == IEDirty)
  379. markbloomfilter(mainindex->bloom, score);
  380. return 0;
  381. }
  382. static int
  383. lookupscore_untimed(u8int score[VtScoreSize], int type, IAddr *ia)
  384. {
  385. IEntry d;
  386. if(icachelookup(score, type, ia) >= 0)
  387. return 0;
  388. addstat(StatIcacheFill, 1);
  389. if(loadientry(mainindex, score, type, &d) < 0)
  390. return -1;
  391. insertscore(score, &d.ia, IEClean, nil);
  392. *ia = d.ia;
  393. return 0;
  394. }
  395. int
  396. lookupscore(u8int score[VtScoreSize], int type, IAddr *ia)
  397. {
  398. int ms, ret;
  399. ms = msec();
  400. ret = lookupscore_untimed(score, type, ia);
  401. ms = msec() - ms;
  402. addstat2(StatIcacheRead, 1, StatIcacheReadTime, ms);
  403. return ret;
  404. }
  405. u32int
  406. hashbits(u8int *sc, int bits)
  407. {
  408. u32int v;
  409. v = (sc[0] << 24) | (sc[1] << 16) | (sc[2] << 8) | sc[3];
  410. if(bits < 32)
  411. v >>= (32 - bits);
  412. return v;
  413. }
  414. ulong
  415. icachedirtyfrac(void)
  416. {
  417. return (vlong)icache.ndirty*IcacheFrac / icache.nentries;
  418. }
  419. /*
  420. * Return a singly-linked list of dirty index entries.
  421. * with 32-bit hash numbers between lo and hi
  422. * and address < limit.
  423. */
  424. IEntry*
  425. icachedirty(u32int lo, u32int hi, u64int limit)
  426. {
  427. u32int h;
  428. IEntry *ie, *dirty;
  429. dirty = nil;
  430. trace(TraceProc, "icachedirty enter");
  431. qlock(&icache.lock);
  432. for(ie = icache.dirty.next; ie != &icache.dirty; ie=ie->next){
  433. if(ie->state == IEDirty && ie->ia.addr < limit){
  434. h = hashbits(ie->score, 32);
  435. if(lo <= h && h <= hi){
  436. ie->nextdirty = dirty;
  437. dirty = ie;
  438. }
  439. }
  440. }
  441. qunlock(&icache.lock);
  442. trace(TraceProc, "icachedirty exit");
  443. if(dirty == nil)
  444. flushdcache();
  445. return dirty;
  446. }
  447. AState
  448. icachestate(void)
  449. {
  450. AState as;
  451. qlock(&icache.lock);
  452. as = icache.as;
  453. qunlock(&icache.lock);
  454. return as;
  455. }
  456. /*
  457. * The singly-linked non-circular list of index entries ie
  458. * has been written to disk. Move them to the clean list.
  459. */
  460. void
  461. icacheclean(IEntry *ie)
  462. {
  463. IEntry *next;
  464. trace(TraceProc, "icacheclean enter");
  465. qlock(&icache.lock);
  466. for(; ie; ie=next){
  467. assert(ie->state == IEDirty);
  468. next = ie->nextdirty;
  469. ie->nextdirty = nil;
  470. popout(ie); /* from icache.dirty */
  471. icache.ndirty--;
  472. ie->state = IEClean;
  473. pushfirst(&icache.clean, ie);
  474. }
  475. setstat(StatIcacheDirty, icache.ndirty);
  476. rwakeupall(&icache.full);
  477. qunlock(&icache.lock);
  478. trace(TraceProc, "icacheclean exit");
  479. }
  480. void
  481. emptyicache(void)
  482. {
  483. int i;
  484. IEntry *ie;
  485. ISum *s;
  486. qlock(&icache.lock);
  487. while((ie = evictlru()) != nil)
  488. pushfirst(&icache.free, ie);
  489. for(i=0; i<icache.nsum; i++){
  490. s = icache.sum[i];
  491. qlock(&s->lock);
  492. sumclear(s);
  493. qunlock(&s->lock);
  494. }
  495. qunlock(&icache.lock);
  496. }