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