1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
|
/* CACHE.H (c)Copyright Greg Smith, 2002-2009 */
/* Buffer Cache Manager */
/*-------------------------------------------------------------------
Description:
Manages multiple caches in a multi-threaded environment. A cache
is dynamically created and destroyed. It's size or number of
entries is also dynamically determined. A cache entry contains
an identifying `key', `flags' which indicate whether an entry is
busy or not, and a `buf' which is a pointer to the cached object.
Cache entry:
The structure of a cache entry is:
U64 key;
U32 flag;
int len;
void *buf;
int value;
U64 age;
The first 8 bits of the flag indicates if the entry is `busy' or
not. If any of the first 8 bits are non-zero then the entry is
considered `busy' and will not be stolen or otherwise reused.
APIs:
General query functions:
int cache_nbr(int ix); [0]
Number of entries
int cache_busy(int ix);
Number of busy entries
int cache_empty(int ix);
Number of empty entries [1]
int cache_waiters(int ix);
Number of waiters for a non-busy cache entry
long long cache_size(int ix);
Size of all allocated objects
long long cache_hits(int ix);
Number of successful lookups
long long cache_misses(int ix);
Number of unsuccessful lookups
int cache_busy_percent(int ix);
Percentage (0 .. 100) of entries that are busy
int cache_empty_percent(int ix);
Percentage of entries that are empty [1]
int cache_hit_percent(int ix);
Percentage of successful lookups to total lookups
Notes [0] `ix' identifies the cache. This is an integer
and is reserved in `cache.h'
[1] An empty entry contains a zero key value.
A valid key should not be all zeroes
(0x0000000000000000) or all ones
(0xffffffffffffffff). All ones is used to
indicate an error circumstance.
Entry specific functions:
U64 cache_getkey(int ix, int i); [0]
Return key for the specified cache entry
U64 cache_setkey(int ix, int i, U64 key);
Set the key for the specified cache entry;
the old key is returned
U32 cache_getflag(int ix, int i);
Return the flag for the specified cache entry
U32 cache_setflag(int ix, int i, U32 andbits, U32 orbits);
Set the flag for the specified cache entry; first
the `andbits' value is `and'ed against the entry then
the `orbits' value is `or'ed against the entry. The
old flag is returned.
U64 cache_getage(int ix, int i); [1]
Return age for the specified cache entry
U64 cache_setage(int ix, int i);
Set age for the specified cache entry
void *cache_getbuf(int ix, int i, int len);
Return address of the object buf for the cache entry.
If `len' is non-zero, then if the current object
is null or `len' is greater than the current object
length then the old object is freed and a new
object is obtained.
void *cache_setbuf(int ix, int i, void *buf, int len);
The old object address and length is replaced.
The address of the old object is returned and
can be freed using `free()'.
int cache_getlen(int ix, int i);
Return the length of the current object
Notes [0] `i' is the index of the entry in cache `ix'
[1] `age' is a sequentially incremented value and
does not correspond to date or time
Locking functions:
int cache_lock(int ix);
Obtain the lock for cache `ix'. If the cache does
not exist then it will be created. Generally, the
lock should be obtained when referencing cache
entries and must be held when a cache entry status
may change from `busy' to `not busy' or vice versa.
Likewise, the lock must be held when a cache entry
changes from `empty' to `not empty' or vice versa.
int cache_unlock(int ix);
Release the cache lock
Search functions:
int cache_lookup(int ix, U64 key, int *o);
Search cache `ix' for entry matching `key'.
If a non-NULL pointer `o' is provided, then the
oldest or preferred cache entry index is returned
that is available to be stolen.
int cache_scan (int ix, int (rtn)(), void *data);
Scan a cache routine entry by entry calling routine
`rtn'. Parameters passed to the routine are
`(int *answer, int ix, int i, void *data)' where
`ix' is the cache index, `i' is the cache entry
index and `data' is the value passed to cache_scan.
`*answer' is initialized to -1 and can be set by
the scan subroutine. This will be the value returned
by cache_scan. If the routine returns a non-zero
value then the scan is terminated.
Other functions:
int cache_wait(int ix);
Wait for a non-busy cache entry to become available.
Typically called after `cache_lookup' was
unsuccessful and `*o' is -1.
int cache_release(int ix, int i, int flag);
Release the cache entry. If flag is CACHE_FREEBUF
then the object buffer is also freed.
int cache_cmd(int argc, char *argv[], char *cmdline);
Interface with the cache command processor. This
interface is subject to change.
-------------------------------------------------------------------*/
#ifndef _HERCULES_CACHE_H
#define _HERCULES_CACHE_H 1
#include "hercules.h"
#ifndef _CACHE_C_
#ifndef _HDASD_DLL_
#define CCH_DLL_IMPORT DLL_IMPORT
#else /* _HDASD_DLL_ */
#define CCH_DLL_IMPORT extern
#endif /* _HDASD_DLL_ */
#else
#define CCH_DLL_IMPORT DLL_EXPORT
#endif
/*-------------------------------------------------------------------*/
/* Reserve cache indexes here */
/*-------------------------------------------------------------------*/
#define CACHE_MAX_INDEX 8 /* Max number caches [0..7] */
#define CACHE_DEVBUF 0 /* Device Buffer cache */
#define CACHE_L2 1 /* L2 cache */
#define CACHE_2 2 /* (available) */
#define CACHE_3 3 /* (available) */
#define CACHE_4 4 /* (available) */
#define CACHE_5 5 /* (available) */
#define CACHE_6 6 /* (available) */
#define CACHE_7 7 /* (available) */
#ifdef _CACHE_C_
/*-------------------------------------------------------------------*/
/* Cache entry */
/*-------------------------------------------------------------------*/
typedef struct _CACHE { /* Cache entry */
U64 key; /* Key */
U32 flag; /* Flags */
int len; /* Buffer length */
void *buf; /* Buffer address */
int value; /* Arbitrary value */
U64 age; /* Age */
} CACHE;
/*-------------------------------------------------------------------*/
/* Cache header */
/*-------------------------------------------------------------------*/
typedef struct _CACHEBLK { /* Cache header */
int magic; /* Magic number */
int nbr; /* Number entries */
int busy; /* Number busy entries */
int empty; /* Number empty entries */
int waiters; /* Number waiters */
int waits; /* Number times waited */
long long size; /* Allocated buffer size */
long long hits; /* Number lookup hits */
long long fasthits; /* Number fast lookup hits */
long long misses; /* Number lookup misses */
U64 age; /* Age counter */
LOCK lock; /* Lock */
COND waitcond; /* Wait for available entry */
CACHE *cache; /* Cache table address */
time_t atime; /* Time last adjustment */
time_t wtime; /* Time last wait */
int adjusts; /* Number of adjustments */
} CACHEBLK;
#endif
/*-------------------------------------------------------------------*/
/* Flag definitions */
/*-------------------------------------------------------------------*/
#define CACHE_BUSY 0xFF000000 /* Busy bits */
#define CACHE_TYPE 0x000000FF /* Type bits */
#define CACHE_FREEBUF 1 /* Free buf on release */
#ifdef _CACHE_C_
#define CACHE_MAGIC 0x01CACE10 /* Magic number */
#define CACHE_DEFAULT_NBR 229 /* Initial entries (prime) */
//FIXME the line below increases the size for CACHE_L2. Since each
// cckd device always has an active l2 entry this number
// actually limits the number of cckd devices that can be
// attached.
// This is a workaround to increase the max number of devices
#define CACHE_DEFAULT_L2_NBR 1031 /* Initial entries for L2 */
#define CACHE_WAITTIME 1000 /* Wait time for entry(usec) */
#define CACHE_ADJUST_INTERVAL 15 /* Adjustment interval (sec) */
#define CACHE_ADJUST_NUMBER 128 /* Uninhibited nbr entries */
#define CACHE_ADJUST_BUSY1 70 /* Increase when this busy 1 */
#define CACHE_ADJUST_BUSY2 80 /* Increase when this busy 2 */
#define CACHE_ADJUST_RESIZE 8 /* Nbr entries adjusted */
#define CACHE_ADJUST_EMPTY 16 /* Decrease this many empty */
#define CACHE_ADJUST_HIT1 60 /* Increase hit% this low 1 */
#define CACHE_ADJUST_HIT2 50 /* Increase hit% this low 2 */
#define CACHE_ADJUST_BUSY3 20 /* Decrease not this busy */
#define CACHE_ADJUST_HIT3 90 /* and hit% this high */
#define CACHE_ADJUST_SIZE (8*1024*1024)/* and size this high */
#define CACHE_ADJUST_WAITTIME 10 /* Increase last wait (sec) */
#endif
/*-------------------------------------------------------------------*/
/* Functions */
/*-------------------------------------------------------------------*/
int cache_nbr(int ix);
int cache_busy(int ix);
int cache_empty(int ix);
int cache_waiters(int ix);
long long cache_size(int ix);
long long cache_hits(int ix);
long long cache_misses(int ix);
int cache_busy_percent(int ix);
int cache_empty_percent(int ix);
int cache_hit_percent(int ix);
int cache_lookup(int ix, U64 key, int *o);
typedef int CACHE_SCAN_RTN (int *answer, int ix, int i, void *data);
int cache_scan (int ix, CACHE_SCAN_RTN rtn, void *data);
int cache_lock(int ix);
int cache_unlock(int ix);
int cache_wait(int ix);
U64 cache_getkey(int ix, int i);
U64 cache_setkey(int ix, int i, U64 key);
U32 cache_getflag(int ix, int i);
U32 cache_setflag(int ix, int i, U32 andbits, U32 orbits);
U64 cache_getage(int ix, int i);
U64 cache_setage(int ix, int i);
void *cache_getbuf(int ix, int i, int len);
void *cache_setbuf(int ix, int i, void *buf, int len);
int cache_getlen(int ix, int i);
int cache_getval(int ix, int i);
int cache_setval(int ix, int i, int val);
int cache_release(int ix, int i, int flag);
CCH_DLL_IMPORT int cache_cmd(int argc, char *argv[], char *cmdline);
#ifdef _CACHE_C_
static int cache_create (int ix);
static int cache_destroy (int ix);
static int cache_check_ix(int ix);
static int cache_check_cache(int ix);
static int cache_check(int ix, int i);
static int cache_isbusy(int ix, int i);
static int cache_isempty(int ix, int i);
static int cache_adjust(int ix, int n);
#if 0
static int cache_resize (int ix, int n);
#endif
static void cache_allocbuf(int ix, int i, int len);
#endif
/*-------------------------------------------------------------------*/
/* Specific cache definitions (until a better place is found) */
/*-------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* Device buffer definitions */
/*-------------------------------------------------------------------*/
#define CCKD_CACHE_ACTIVE 0x80000000 /* Active entry */
#define CCKD_CACHE_READING 0x40000000 /* Entry being read */
#define CCKD_CACHE_WRITING 0x20000000 /* Entry being written */
#define CCKD_CACHE_IOBUSY (CCKD_CACHE_READING|CCKD_CACHE_WRITING)
#define CCKD_CACHE_IOWAIT 0x10000000 /* Waiters for i/o */
#define CCKD_CACHE_UPDATED 0x08000000 /* Buffer has been updated */
#define CCKD_CACHE_WRITE 0x04000000 /* Entry pending write */
#define CCKD_CACHE_USED 0x00800000 /* Entry has been used */
#define CKD_CACHE_ACTIVE 0x80000000 /* Active entry */
#define FBA_CACHE_ACTIVE 0x80000000 /* Active entry */
#define SHRD_CACHE_ACTIVE 0x80000000 /* Active entry */
#define DEVBUF_TYPE_SHARED 0x00000080 /* Shared entry type */
#define DEVBUF_TYPE_COMP 0x00000040 /* CCKD/CFBA entry type */
#define DEVBUF_TYPE_CKD 0x00000002 /* CKD entry type */
#define DEVBUF_TYPE_FBA 0x00000001 /* FBA entry type */
#define DEVBUF_TYPE_CCKD (DEVBUF_TYPE_COMP|DEVBUF_TYPE_CKD)
#define DEVBUF_TYPE_CFBA (DEVBUF_TYPE_COMP|DEVBUF_TYPE_FBA)
#define DEVBUF_TYPE_SCKD (DEVBUF_TYPE_SHARED|DEVBUF_TYPE_CKD)
#define DEVBUF_TYPE_SFBA (DEVBUF_TYPE_SHARED|DEVBUF_TYPE_FBA)
#define CCKD_CACHE_GETKEY(_ix, _devnum, _trk) \
do { \
(_devnum) = (U16)((cache_getkey(CACHE_DEVBUF,(_ix)) >> 32) & 0xFFFF); \
(_trk) = (U32)(cache_getkey(CACHE_DEVBUF,(_ix)) & 0xFFFFFFFF); \
} while (0)
#define CCKD_CACHE_SETKEY(_devnum, _trk) \
((U64)(((U64)(_devnum) << 32) | (U64)(_trk)))
#define CKD_CACHE_GETKEY(_ix, _devnum, _trk) \
{ \
(_devnum) = (U16)((cache_getkey(CACHE_DEVBUF,(_ix)) >> 32) & 0xFFFF); \
(_trk) = (U32)(cache_getkey(CACHE_DEVBUF,(_ix)) & 0xFFFFFFFF); \
}
#define CKD_CACHE_SETKEY(_devnum, _trk) \
((U64)(((U64)(_devnum) << 32) | (U64)(_trk)))
#define FBA_CACHE_GETKEY(_ix, _devnum, _blkgrp) \
{ \
(_devnum) = (U16)((cache_getkey(CACHE_DEVBUF,(_ix)) >> 32) & 0xFFFF); \
(_blkgrp) = (U32)(cache_getkey(CACHE_DEVBUF,(_ix)) & 0xFFFFFFFF); \
}
#define FBA_CACHE_SETKEY(_devnum, _blkgrp) \
((U64)(((U64)(_devnum) << 32) | (U64)(_blkgrp)))
#define SHRD_CACHE_GETKEY(_ix, _devnum, _trk) \
{ \
(_devnum) = (U16)((cache_getkey(CACHE_DEVBUF,(_ix)) >> 32) & 0xFFFF); \
(_trk) = (U32)(cache_getkey(CACHE_DEVBUF,(_ix)) & 0xFFFFFFFF); \
}
#define SHRD_CACHE_SETKEY(_devnum, _trk) \
((U64)(((U64)(_devnum) << 32) | (U64)(_trk)))
/*-------------------------------------------------------------------*/
/* L2 definitions */
/*-------------------------------------------------------------------*/
#define L2_CACHE_ACTIVE 0x80000000 /* Active entry */
#define L2_CACHE_GETKEY(_ix, _sfx, _devnum, _trk) \
do { \
(_sfx) = (U16)((cache_getkey(CACHE_L2,(_ix)) >> 48) & 0xFFFF); \
(_devnum) = (U16)((cache_getkey(CACHE_L2,(_ix)) >> 32) & 0xFFFF); \
(_trk) = (U32)(cache_getkey(CACHE_L2,(_ix)) & 0xFFFFFFFF); \
} while (0)
#define L2_CACHE_SETKEY(_sfx, _devnum, _trk) \
((U64)(((U64)(_sfx) << 48) | ((U64)(_devnum) << 32) | (U64)(_trk)))
#endif /* _HERCULES_CACHE_H */
|