1 /* CACHE.C (c)Copyright Greg Smith, 2002-2009 */
2 /* Dynamic cache manager for multi-threaded applications */
3
4 //FIXME ?? Dynamic resizing is disabled
5
6 #include "hstdinc.h"
7
8 #define _CACHE_C_
9 #define _HDASD_DLL_
10
11 #include "hercules.h"
12
13 static CACHEBLK cacheblk[CACHE_MAX_INDEX];
14
15 /*-------------------------------------------------------------------*/
16 /* Public functions */
17 /*-------------------------------------------------------------------*/
cache_nbr(int ix)18 int cache_nbr (int ix)
19 {
20 if (cache_check_ix(ix)) return -1;
21 return cacheblk[ix].nbr;
22 }
23
cache_busy(int ix)24 int cache_busy (int ix)
25 {
26 if (cache_check_ix(ix)) return -1;
27 return cacheblk[ix].busy;
28 }
29
cache_empty(int ix)30 int cache_empty (int ix)
31 {
32 if (cache_check_ix(ix)) return -1;
33 return cacheblk[ix].empty;
34 }
35
cache_waiters(int ix)36 int cache_waiters (int ix)
37 {
38 if (cache_check_ix(ix)) return -1;
39 return cacheblk[ix].waiters;
40 }
41
cache_size(int ix)42 long long cache_size (int ix)
43 {
44 if (cache_check_ix(ix)) return -1;
45 return cacheblk[ix].size;
46 }
47
cache_hits(int ix)48 long long cache_hits (int ix)
49 {
50 if (cache_check_ix(ix)) return -1;
51 return cacheblk[ix].hits;
52 }
53
cache_misses(int ix)54 long long cache_misses (int ix)
55 {
56 if (cache_check_ix(ix)) return -1;
57 return cacheblk[ix].misses;
58 }
59
cache_busy_percent(int ix)60 int cache_busy_percent (int ix)
61 {
62 if (cache_check_ix(ix)) return -1;
63 return (cacheblk[ix].busy * 100) / cacheblk[ix].nbr;
64 }
65
cache_empty_percent(int ix)66 int cache_empty_percent (int ix)
67 {
68 if (cache_check_ix(ix)) return -1;
69 return (cacheblk[ix].empty * 100) / cacheblk[ix].nbr;
70 }
71
cache_hit_percent(int ix)72 int cache_hit_percent (int ix)
73 {
74 long long total;
75 if (cache_check_ix(ix)) return -1;
76 total = cacheblk[ix].hits + cacheblk[ix].misses;
77 if (total == 0) return -1;
78 return (int)((cacheblk[ix].hits * 100) / total);
79 }
80
cache_lookup(int ix,U64 key,int * o)81 int cache_lookup (int ix, U64 key, int *o)
82 {
83 int i,p;
84 if (o) *o = -1;
85 if (cache_check_ix(ix)) return -1;
86 /* `p' is the preferred index */
87 p = (int)(key % cacheblk[ix].nbr);
88 if (cacheblk[ix].cache[p].key == key) {
89 i = p;
90 cacheblk[ix].fasthits++;
91 }
92 else {
93 if (cache_isbusy(ix, p) || cacheblk[ix].age - cacheblk[ix].cache[p].age < 20)
94 p = -2;
95 for (i = 0; i < cacheblk[ix].nbr; i++) {
96 if (cacheblk[ix].cache[i].key == key) break;
97 if (o && !cache_isbusy(ix, i)
98 && (*o < 0 || i == p || cacheblk[ix].cache[i].age < cacheblk[ix].cache[*o].age))
99 if (*o != p) *o = i;
100 }
101 }
102 if (i >= cacheblk[ix].nbr) {
103 i = -1;
104 cacheblk[ix].misses++;
105 }
106 else
107 cacheblk[ix].hits++;
108
109 if (i < 0 && o && *o < 0) cache_adjust(ix,1);
110 else cache_adjust(ix, 0);
111 return i;
112 }
113
cache_scan(int ix,CACHE_SCAN_RTN rtn,void * data)114 int cache_scan (int ix, CACHE_SCAN_RTN rtn, void *data)
115 {
116 int i; /* Cache index */
117 int rc; /* Return code */
118 int answer = -1; /* Answer from routine */
119
120 if (cache_check_ix(ix)) return -1;
121 for (i = 0; i < cacheblk[ix].nbr; i++) {
122 rc = (rtn)(&answer, ix, i, data);
123 if (rc != 0) break;
124 }
125 return answer;
126 }
127
cache_lock(int ix)128 int cache_lock(int ix)
129 {
130 if (cache_check_cache(ix)) return -1;
131 obtain_lock(&cacheblk[ix].lock);
132 return 0;
133 }
134
cache_unlock(int ix)135 int cache_unlock(int ix)
136 {
137 if (cache_check_ix(ix)) return -1;
138 release_lock(&cacheblk[ix].lock);
139 if (cacheblk[ix].empty == cacheblk[ix].nbr)
140 cache_destroy(ix);
141 return 0;
142 }
143
cache_wait(int ix)144 int cache_wait(int ix)
145 {
146 struct timeval now;
147 struct timespec tm;
148
149 if (cache_check_ix(ix)) return -1;
150 if (cacheblk[ix].busy < cacheblk[ix].nbr)
151 return 0;
152 if (cache_adjust(ix, 1))
153 return 0;
154 gettimeofday (&now, NULL);
155 tm.tv_sec = now.tv_sec;
156 tm.tv_nsec = (now.tv_usec + CACHE_WAITTIME) * 1000;
157 tm.tv_sec += tm.tv_nsec / 1000000000;
158 tm.tv_nsec = tm.tv_nsec % 1000000000;
159 cacheblk[ix].waiters++; cacheblk[ix].waits++;
160 #if 0
161 timed_wait_condition(&cacheblk[ix].waitcond, &cacheblk[ix].lock, &tm);
162 #else
163 wait_condition(&cacheblk[ix].waitcond, &cacheblk[ix].lock);
164 #endif
165 cacheblk[ix].waiters--;
166 return 0;
167 }
168
cache_getkey(int ix,int i)169 U64 cache_getkey(int ix, int i)
170 {
171 if (cache_check(ix,i)) return (U64)-1;
172 return cacheblk[ix].cache[i].key;
173 }
174
cache_setkey(int ix,int i,U64 key)175 U64 cache_setkey(int ix, int i, U64 key)
176 {
177 U64 oldkey;
178 int empty;
179
180 if (cache_check(ix,i)) return (U64)-1;
181 empty = cache_isempty(ix, i);
182 oldkey = cacheblk[ix].cache[i].key;
183 cacheblk[ix].cache[i].key = key;
184 if (empty && !cache_isempty(ix, i))
185 cacheblk[ix].empty--;
186 else if (!empty && cache_isempty(ix, i))
187 cacheblk[ix].empty++;
188 return oldkey;
189 }
190
cache_getflag(int ix,int i)191 U32 cache_getflag(int ix, int i)
192 {
193 if (cache_check(ix,i)) return (U32)-1;
194 return cacheblk[ix].cache[i].flag;
195 }
196
cache_setflag(int ix,int i,U32 andbits,U32 orbits)197 U32 cache_setflag(int ix, int i, U32 andbits, U32 orbits)
198 {
199 U32 oldflags;
200 int empty;
201 int busy;
202
203 if (cache_check(ix,i)) return (U32)-1;
204
205 empty = cache_isempty(ix, i);
206 busy = cache_isbusy(ix, i);
207 oldflags = cacheblk[ix].cache[i].flag;
208
209 cacheblk[ix].cache[i].flag &= andbits;
210 cacheblk[ix].cache[i].flag |= orbits;
211
212 if (!cache_isbusy(ix, i) && cacheblk[ix].waiters > 0)
213 signal_condition(&cacheblk[ix].waitcond);
214 if (busy && !cache_isbusy(ix, i))
215 cacheblk[ix].busy--;
216 else if (!busy && cache_isbusy(ix, i))
217 cacheblk[ix].busy++;
218 if (empty && !cache_isempty(ix, i))
219 cacheblk[ix].empty--;
220 else if (!empty && cache_isempty(ix, i))
221 cacheblk[ix].empty++;
222 return oldflags;
223 }
224
cache_getage(int ix,int i)225 U64 cache_getage(int ix, int i)
226 {
227 if (cache_check(ix,i)) return (U64)-1;
228 return cacheblk[ix].cache[i].age;
229 }
230
cache_setage(int ix,int i)231 U64 cache_setage(int ix, int i)
232 {
233 U64 oldage;
234 int empty;
235
236 if (cache_check(ix,i)) return (U64)-1;
237 empty = cache_isempty(ix, i);
238 oldage = cacheblk[ix].cache[i].age;
239 cacheblk[ix].cache[i].age = ++cacheblk[ix].age;
240 if (empty) cacheblk[ix].empty--;
241 return oldage;
242 }
243
cache_getbuf(int ix,int i,int len)244 void *cache_getbuf(int ix, int i, int len)
245 {
246 if (cache_check(ix,i)) return NULL;
247 if (len > 0
248 && cacheblk[ix].cache[i].buf != NULL
249 && cacheblk[ix].cache[i].len < len) {
250 cacheblk[ix].size -= cacheblk[ix].cache[i].len;
251 free (cacheblk[ix].cache[i].buf);
252 cacheblk[ix].cache[i].buf = NULL;
253 cacheblk[ix].cache[i].len = 0;
254 }
255 if (cacheblk[ix].cache[i].buf == NULL && len > 0)
256 cache_allocbuf (ix, i, len);
257 return cacheblk[ix].cache[i].buf;
258 }
259
cache_setbuf(int ix,int i,void * buf,int len)260 void *cache_setbuf(int ix, int i, void *buf, int len)
261 {
262 void *oldbuf;
263 if (cache_check(ix,i)) return NULL;
264 oldbuf = cacheblk[ix].cache[i].buf;
265 cacheblk[ix].size -= cacheblk[ix].cache[i].len;
266 cacheblk[ix].cache[i].buf = buf;
267 cacheblk[ix].cache[i].len = len;
268 cacheblk[ix].size += len;
269 return oldbuf;
270 }
271
cache_getlen(int ix,int i)272 int cache_getlen(int ix, int i)
273 {
274 if (cache_check(ix,i)) return -1;
275 return cacheblk[ix].cache[i].len;
276 }
277
cache_getval(int ix,int i)278 int cache_getval(int ix, int i)
279 {
280 if (cache_check(ix,i)) return -1;
281 return cacheblk[ix].cache[i].value;
282 }
283
cache_setval(int ix,int i,int val)284 int cache_setval(int ix, int i, int val)
285 {
286 int rc;
287 if (cache_check(ix,i)) return -1;
288 rc = cacheblk[ix].cache[i].value;
289 cacheblk[ix].cache[i].value = val;
290 return rc;
291 }
292
cache_release(int ix,int i,int flag)293 int cache_release(int ix, int i, int flag)
294 {
295 void *buf;
296 int len;
297 int empty;
298 int busy;
299
300 if (cache_check(ix,i)) return -1;
301
302 empty = cache_isempty(ix, i);
303 busy = cache_isbusy(ix, i);
304
305 buf = cacheblk[ix].cache[i].buf;
306 len = cacheblk[ix].cache[i].len;
307
308 memset (&cacheblk[ix].cache[i], 0, sizeof(CACHE));
309
310 if ((flag & CACHE_FREEBUF) && buf != NULL) {
311 free (buf);
312 cacheblk[ix].size -= len;
313 buf = NULL;
314 len = 0;
315 }
316
317 cacheblk[ix].cache[i].buf = buf;
318 cacheblk[ix].cache[i].len = len;
319
320 if (cacheblk[ix].waiters > 0)
321 signal_condition(&cacheblk[ix].waitcond);
322
323 if (!empty) cacheblk[ix].empty++;
324 if (busy) cacheblk[ix].busy--;
325
326 return 0;
327 }
328
cache_cmd(int argc,char * argv[],char * cmdline)329 DLL_EXPORT int cache_cmd(int argc, char *argv[], char *cmdline)
330 {
331 int ix, i;
332
333 UNREFERENCED(cmdline);
334 UNREFERENCED(argc);
335 UNREFERENCED(argv);
336
337 for (ix = 0; ix < CACHE_MAX_INDEX; ix++) {
338 if (cacheblk[ix].magic != CACHE_MAGIC) {
339 logmsg ("cache[%d] ....... not created\n", ix);
340 continue;
341 }
342 logmsg ("\n"
343 "cache............ %10d\n"
344 "nbr ............. %10d\n"
345 "busy ............ %10d\n"
346 "busy%% ........... %10d\n"
347 "empty ........... %10d\n"
348 "waiters ......... %10d\n"
349 "waits ........... %10d\n"
350 "buf size ........ %10" I64_FMT "d\n"
351 "hits ............ %10" I64_FMT "d\n"
352 "fast hits ....... %10" I64_FMT "d\n"
353 "misses .......... %10" I64_FMT "d\n"
354 "hit%% ............ %10d\n"
355 "age ............. %10" I64_FMT "d\n"
356 "last adjusted ... %s"
357 "last wait ....... %s"
358 "adjustments ..... %10d\n",
359 ix, cacheblk[ix].nbr, cacheblk[ix].busy, cache_busy_percent(ix),
360 cacheblk[ix].empty, cacheblk[ix].waiters, cacheblk[ix].waits,
361 cacheblk[ix].size, cacheblk[ix].hits, cacheblk[ix].fasthits,
362 cacheblk[ix].misses, cache_hit_percent(ix), cacheblk[ix].age,
363 ctime(&cacheblk[ix].atime), ctime(&cacheblk[ix].wtime),
364 cacheblk[ix].adjusts);
365 if (argc > 1)
366 for (i = 0; i < cacheblk[ix].nbr; i++)
367 logmsg ("[%4d] %16.16" I64_FMT "x %8.8x %10p %6d %10" I64_FMT "d\n",
368 i, cacheblk[ix].cache[i].key, cacheblk[ix].cache[i].flag,
369 cacheblk[ix].cache[i].buf, cacheblk[ix].cache[i].len,
370 cacheblk[ix].cache[i].age);
371 }
372 return 0;
373 }
374
375 /*-------------------------------------------------------------------*/
376 /* Private functions */
377 /*-------------------------------------------------------------------*/
cache_create(int ix)378 static int cache_create (int ix)
379 {
380 cache_destroy (ix);
381 cacheblk[ix].magic = CACHE_MAGIC;
382 //FIXME See the note in cache.h about CACHE_DEFAULT_L2_NBR
383 cacheblk[ix].nbr = ix != CACHE_L2 ? CACHE_DEFAULT_NBR : CACHE_DEFAULT_L2_NBR;
384 cacheblk[ix].empty = cacheblk[ix].nbr;
385 initialize_lock (&cacheblk[ix].lock);
386 initialize_condition (&cacheblk[ix].waitcond);
387 cacheblk[ix].cache = calloc (cacheblk[ix].nbr, sizeof(CACHE));
388 if (cacheblk[ix].cache == NULL) {
389 logmsg (_("HHCCH001E calloc failed cache[%d] size %d: %s\n"),
390 ix, cacheblk[ix].nbr * sizeof(CACHE), strerror(errno));
391 return -1;
392 }
393 return 0;
394 }
395
cache_destroy(int ix)396 static int cache_destroy (int ix)
397 {
398 int i;
399 if (cacheblk[ix].magic == CACHE_MAGIC) {
400 destroy_lock (&cacheblk[ix].lock);
401 destroy_condition (&cacheblk[ix].waitcond);
402 if (cacheblk[ix].cache) {
403 for (i = 0; i < cacheblk[ix].nbr; i++)
404 cache_release(ix, i, CACHE_FREEBUF);
405 free (cacheblk[ix].cache);
406 }
407 }
408 memset(&cacheblk[ix], 0, sizeof(CACHEBLK));
409 return 0;
410 }
411
cache_check_ix(int ix)412 static int cache_check_ix(int ix)
413 {
414 if (ix < 0 || ix >= CACHE_MAX_INDEX) return -1;
415 return 0;
416 }
417
cache_check_cache(int ix)418 static int cache_check_cache(int ix)
419 {
420 if (cache_check_ix(ix)
421 || (cacheblk[ix].magic != CACHE_MAGIC && cache_create(ix)))
422 return -1;
423 return 0;
424 }
425
cache_check(int ix,int i)426 static int cache_check(int ix, int i)
427 {
428 if (cache_check_ix(ix) || i < 0 || i >= cacheblk[ix].nbr)
429 return -1;
430 return 0;
431 }
432
cache_isbusy(int ix,int i)433 static int cache_isbusy(int ix, int i)
434 {
435 return ((cacheblk[ix].cache[i].flag & CACHE_BUSY) != 0);
436 }
437
cache_isempty(int ix,int i)438 static int cache_isempty(int ix, int i)
439 {
440 return (cacheblk[ix].cache[i].key == 0
441 && cacheblk[ix].cache[i].flag == 0
442 && cacheblk[ix].cache[i].age == 0);
443 }
444
cache_adjust(int ix,int n)445 static int cache_adjust(int ix, int n)
446 {
447 #if 0
448 time_t now;
449 int busypct, hitpct, nbr, empty, sz;
450
451 now = time(NULL);
452 busypct = cache_busy_percent(ix);
453 hitpct = cache_hit_percent(ix);
454 nbr = cacheblk[ix].nbr;
455 empty = cache_empty(ix);
456 sz = cacheblk[ix].size;
457
458 if (n == 0) {
459 /* Normal adjustments */
460 if (now - cacheblk[ix].atime < CACHE_ADJUST_INTERVAL) return 0;
461 cacheblk[ix].atime = now;
462
463 /* Increase cache if a lot of busy entries */
464 if (((nbr <= CACHE_ADJUST_NUMBER || sz < CACHE_ADJUST_SIZE) && busypct >= CACHE_ADJUST_BUSY1)
465 || busypct > CACHE_ADJUST_BUSY2)
466 return cache_resize(ix, CACHE_ADJUST_RESIZE);
467
468 /* Decrease cache if too many empty entries */
469 if (nbr > CACHE_ADJUST_NUMBER && empty >= CACHE_ADJUST_EMPTY)
470 return cache_resize(ix, -CACHE_ADJUST_RESIZE);
471
472 /* Increase cache if hit percentage is too low */
473 if (hitpct > 0) {
474 if ((nbr <= CACHE_ADJUST_NUMBER && hitpct < CACHE_ADJUST_HIT1)
475 || hitpct < CACHE_ADJUST_HIT2)
476 return cache_resize(ix, CACHE_ADJUST_RESIZE);
477 }
478
479 /* Decrease cache if hit percentage is ok and not many busy */
480 if (hitpct >= CACHE_ADJUST_HIT3 && busypct <= CACHE_ADJUST_BUSY3
481 && cacheblk[ix].size >= CACHE_ADJUST_SIZE)
482 return cache_resize(ix, -CACHE_ADJUST_RESIZE);
483 } else {
484 /* All cache entries are busy */
485 if (nbr <= CACHE_ADJUST_NUMBER)
486 return cache_resize(ix, CACHE_ADJUST_RESIZE);
487
488 /* Increase cache if previous wait within this interval */
489 if (now - cacheblk[ix].wtime <= CACHE_ADJUST_WAITTIME) {
490 return cache_resize(ix, CACHE_ADJUST_RESIZE);
491 }
492 cacheblk[ix].wtime = now;
493 }
494 #else
495 UNREFERENCED(ix);
496 UNREFERENCED(n);
497 #endif
498 return 0;
499 }
500
501 #if 0
502 static int cache_resize (int ix, int n)
503 {
504 CACHE *cache;
505 int i;
506
507 if (n == 0) return 0;
508 else if (n > 0) {
509 /* Increase cache size */
510 cache = realloc (cacheblk[ix].cache, (cacheblk[ix].nbr + n) * sizeof(CACHE));
511 if (cache == NULL) {
512 logmsg (_("HHCCH002W realloc increase failed cache[%d] size %d: %s\n"),
513 ix, (cacheblk[ix].nbr + n) * sizeof(CACHE), strerror(errno));
514 return 0;
515 }
516 cacheblk[ix].cache = cache;
517 for (i = cacheblk[ix].nbr; i < cacheblk[ix].nbr +n; i++)
518 memset(&cacheblk[ix].cache[i], 0, sizeof(CACHE));
519 cacheblk[ix].nbr += n;
520 cacheblk[ix].empty += n;
521 cacheblk[ix].adjusts++;
522 } else if (n < 0) {
523 /* Decrease cache size */
524 for (i = cacheblk[ix].nbr - 1; i >= cacheblk[ix].nbr + n; i--)
525 if (cache_isbusy(ix, i)) break;
526 else cache_release(ix, i, CACHE_FREEBUF);
527 n = cacheblk[ix].nbr - i + 1;
528 if (n == 0) return 0;
529 cache = realloc (cacheblk[ix].cache, (cacheblk[ix].nbr - n) * sizeof(CACHE));
530 if (cache == NULL) {
531 logmsg (_("HHCCH003W realloc decrease failed cache[%d] size %d: %s\n"),
532 ix, (cacheblk[ix].nbr - n) * sizeof(CACHE), strerror(errno));
533 return 0;
534 }
535 cacheblk[ix].cache = cache;
536 cacheblk[ix].nbr -= n;
537 cacheblk[ix].empty -= n;
538 cacheblk[ix].adjusts++;
539 }
540 return 1;
541 }
542 #endif
543
cache_allocbuf(int ix,int i,int len)544 static void cache_allocbuf(int ix, int i, int len)
545 {
546 cacheblk[ix].cache[i].buf = calloc (len, 1);
547 if (cacheblk[ix].cache[i].buf == NULL) {
548 logmsg (_("HHCCH004W buf calloc failed cache[%d] size %d: %s\n"),
549 ix, len, strerror(errno));
550 logmsg (_("HHCCH005W releasing inactive buffer space\n"));
551 for (i = 0; i < cacheblk[ix].nbr; i++)
552 if (!cache_isbusy(ix, i)) cache_release(ix, i, CACHE_FREEBUF);
553 cacheblk[ix].cache[i].buf = calloc (len, 1);
554 if (cacheblk[ix].cache[i].buf == NULL) {
555 logmsg (_("HHCCH006E Unable to calloc buf cache[%d] size %d: %s\n"),
556 ix, len, strerror(errno));
557 return;
558 }
559 }
560 cacheblk[ix].cache[i].len = len;
561 cacheblk[ix].size += len;
562 }
563