00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #ifndef _MultiCache_h_
00033 #define _MultiCache_h_
00034
00035 #include "I_EventSystem.h"
00036 #include "I_Store.h"
00037
00038
00039
00040
00041
00042 #define MULTI_CACHE_MAX_LEVELS 3
00043 #define MULTI_CACHE_MAX_BUCKET_SIZE 256
00044 #define MULTI_CACHE_MAX_FILES 256
00045 #define MULTI_CACHE_PARTITIONS 64
00046
00047 #define MULTI_CACHE_EVENT_SYNC MULTI_CACHE_EVENT_EVENTS_START
00048
00049
00050
00051 #define MULTI_CACHE_HEAP_NONE -1
00052
00053 #define MULTI_CACHE_MAGIC_NUMBER 0x0BAD2D8
00054
00055
00056
00057 #define MULTI_CACHE_MAJOR_VERSION 2
00058 #define MULTI_CACHE_MINOR_VERSION 1
00059
00060
00061 #define MULTI_CACHE_HEAP_HIGH_WATER 0.8
00062
00063 #define MULTI_CACHE_HEAP_INITIAL sizeof(uint32_t)
00064 #define MULTI_CACHE_HEAP_ALIGNMENT 8
00065
00066
00067 #define MULTI_CACHE_OFFSET_PARITION(_x) ((_x)%MULTI_CACHE_PARTITIONS)
00068 #define MULTI_CACHE_OFFSET_INDEX(_x) ((_x)/MULTI_CACHE_PARTITIONS)
00069 #define MULTI_CACHE_OFFSET(_p,_o) ((_p) + (_o) * MULTI_CACHE_PARTITIONS)
00070
00071 class ProxyMutex;
00072 class Continuation;
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083 struct MultiCacheBlock
00084 {
00085 uint64_t tag();
00086 bool is_deleted();
00087 void set_deleted();
00088 bool is_empty();
00089 void set_empty();
00090 void reset();
00091 void set_full(uint64_t folded_md5, int buckets);
00092 int heap_size()
00093 {
00094 return 0;
00095 }
00096 int *heap_offset_ptr()
00097 {
00098 return NULL;
00099 }
00100 };
00101
00102 struct RebuildMC
00103 {
00104 bool rebuild;
00105 bool check;
00106 bool fix;
00107 char *data;
00108 int partition;
00109
00110 int deleted;
00111 int backed;
00112 int duplicates;
00113 int corrupt;
00114 int stale;
00115 int good;
00116 int total;
00117 };
00118
00119 struct MultiCacheHeader
00120 {
00121 unsigned int magic;
00122 VersionNumber version;
00123
00124 int levels;
00125
00126 int tag_bits;
00127 int max_hits;
00128 int elementsize;
00129
00130 int buckets;
00131 int level_offset[MULTI_CACHE_MAX_LEVELS];
00132 int elements[MULTI_CACHE_MAX_LEVELS];
00133 int bucketsize[MULTI_CACHE_MAX_LEVELS];
00134
00135 int totalelements;
00136 unsigned int totalsize;
00137
00138 int nominal_elements;
00139
00140
00141 int heap_size;
00142 volatile int heap_halfspace;
00143 volatile int heap_used[2];
00144
00145 MultiCacheHeader();
00146 };
00147
00148
00149
00150 #define MULTI_CACHE_UNSUNK_PTR_BLOCK_SIZE(_e) \
00151 ((_e / 8) / MULTI_CACHE_PARTITIONS)
00152
00153 struct UnsunkPtr
00154 {
00155 int offset;
00156 int *poffset;
00157 };
00158
00159 struct MultiCacheBase;
00160
00161 struct UnsunkPtrRegistry
00162 {
00163 MultiCacheBase *mc;
00164 int n;
00165 UnsunkPtr *ptrs;
00166 UnsunkPtr *next_free;
00167 UnsunkPtrRegistry *next;
00168
00169 UnsunkPtr *ptr(int i);
00170 UnsunkPtr *alloc(int *p, int base = 0);
00171 void alloc_data();
00172
00173 UnsunkPtrRegistry();
00174 ~UnsunkPtrRegistry();
00175 };
00176
00177
00178
00179
00180 #define PtrMutex Ptr<ProxyMutex>
00181
00182
00183
00184
00185
00186 struct Unmaper
00187 {
00188 void *hMap;
00189 char *pAddr;
00190 };
00191
00192 typedef int three_ints[3];
00193 typedef int two_ints[2];
00194
00195 struct MultiCacheHeapGC;
00196
00197 struct MultiCacheBase: public MultiCacheHeader
00198 {
00199 Store *store;
00200 char filename[PATH_NAME_MAX + 1];
00201 MultiCacheHeader *mapped_header;
00202
00203 MultiCacheHeader header_snap;
00204
00205
00206
00207 char *data;
00208 char *lowest_level_data;
00209
00210
00211 char *heap;
00212
00213
00214
00215 int halfspace_size()
00216 {
00217 return heap_size / 2;
00218 }
00219
00220
00221
00222 int hit_stat[MULTI_CACHE_MAX_LEVELS];
00223 int miss_stat;
00224
00225 int lowest_level_data_size()
00226 {
00227 return (buckets + 3) / 4;
00228 }
00229 int lowest_level(int bucket)
00230 {
00231 int i = (unsigned char) lowest_level_data[bucket / 4];
00232 return 3 & (i >> (buckets % 4));
00233 }
00234 void set_lowest_level(int bucket, int lowest)
00235 {
00236 unsigned char p = (unsigned char) lowest_level_data[bucket / 4];
00237 p &= ~(3 << (buckets % 4));
00238 p |= (lowest & 3) << (buckets % 4);
00239 lowest_level_data[bucket / 4] = (char) p;
00240 }
00241
00242
00243 int buckets_per_partitionF8;
00244
00245 int partition_of_bucket(int b)
00246 {
00247 return ((b << 8) + 0xFF) / buckets_per_partitionF8;
00248 }
00249 int first_bucket_of_partition(int p)
00250 {
00251 return ((buckets_per_partitionF8 * p) >> 8);
00252 }
00253 int last_bucket_of_partition(int p)
00254 {
00255 return first_bucket_of_partition(p + 1) - 1;
00256 }
00257 int buckets_of_partition(int p)
00258 {
00259 return last_bucket_of_partition(p) - first_bucket_of_partition(p) + 1;
00260 }
00261
00262 int open(Store * store, const char *config_filename,
00263 char *db_filename = NULL, int db_size = -1, bool reconfigure = false, bool fix = false, bool silent = false);
00264
00265
00266 int read_config(const char *config_filename, Store & store, char *fn = NULL, int *pi = NULL, int *pbuckets = NULL);
00267 int write_config(const char *config_filename, int nominal_size, int buckets);
00268 int initialize(Store * store, char *filename, int elements,
00269 int buckets = 0, int levels = 2,
00270 int level0_elements_per_bucket = 4,
00271 int level1_elements_per_bucket = 32, int level2_elements_per_bucket = 1);
00272 int mmap_data(bool private_flag = false, bool zero_fill = false);
00273 char *mmap_region(int blocks, int *fds, char *cur, size_t& total_length, bool private_flag, int zero_fill = 0);
00274 int blocks_in_level(int level);
00275
00276 bool verify_header();
00277
00278 int unmap_data();
00279 void reset();
00280 void clear();
00281 void clear_but_heap();
00282
00283 virtual MultiCacheBase *dup()
00284 {
00285 ink_assert(0);
00286 return NULL;
00287 }
00288
00289 virtual size_t estimated_heap_bytes_per_entry() const { return 0; }
00290
00291 void print_info(FILE * fp);
00292
00293
00294
00295
00296
00297
00298 #define MC_REBUILD 0
00299 #define MC_REBUILD_CHECK 1
00300 #define MC_REBUILD_FIX 2
00301 int rebuild(MultiCacheBase & old, int kind = MC_REBUILD);
00302
00303 virtual void rebuild_element(int buck, char *elem, RebuildMC & r)
00304 {
00305 (void) buck;
00306 (void) elem;
00307 (void) r;
00308 ink_assert(0);
00309 }
00310
00311
00312
00313
00314
00315
00316 int check(const char *config_filename, bool fix = false);
00317
00318 ProxyMutex *lock_for_bucket(int bucket)
00319 {
00320 return locks[partition_of_bucket(bucket)];
00321 }
00322 uint64_t make_tag(uint64_t folded_md5)
00323 {
00324 uint64_t ttag = folded_md5 / (uint64_t) buckets;
00325 if (!ttag)
00326 return 1LL;
00327
00328 if (tag_bits > 32) {
00329 uint64_t mask = 0x100000000LL << (tag_bits - 32);
00330 mask = mask - 1;
00331 return ttag & mask;
00332 } else {
00333 uint64_t mask = 1LL;
00334 mask <<= tag_bits;
00335 mask = mask - 1;
00336 return ttag & mask;
00337 }
00338 }
00339
00340 int sync_all();
00341 int sync_heap(int part);
00342 int sync_header();
00343 int sync_partition(int partition);
00344 void sync_partitions(Continuation * cont);
00345
00346 MultiCacheBase();
00347 virtual ~ MultiCacheBase() {
00348 reset();
00349 }
00350
00351 virtual int get_elementsize()
00352 {
00353 ink_assert(0);
00354 return 0;
00355 }
00356
00357
00358 UnsunkPtrRegistry unsunk[MULTI_CACHE_PARTITIONS];
00359
00360
00361 int ptr_to_partition(char *);
00362
00363
00364
00365
00366 void *alloc(int *poffset, int size);
00367 void update(int *poffset, int *old_poffset);
00368 void *ptr(int *poffset, int partition);
00369 int valid_offset(int offset)
00370 {
00371 int max;
00372 if (offset < halfspace_size())
00373 max = heap_used[0];
00374 else
00375 max = halfspace_size() + heap_used[1];
00376 return offset < max;
00377 }
00378 int valid_heap_pointer(char *p)
00379 {
00380 if (p < heap + halfspace_size())
00381 return p < heap + heap_used[0];
00382 else
00383 return p < heap + halfspace_size() + heap_used[1];
00384 }
00385 void copy_heap_data(char *src, int s, int *pi, int partition, MultiCacheHeapGC * gc);
00386 int halfspace_of(int o)
00387 {
00388 return o < halfspace_size()? 0 : 1;
00389 }
00390 UnsunkPtrRegistry *fixup_heap_offsets(int partition, int before_used, UnsunkPtrRegistry * r = NULL, int base = 0);
00391
00392 virtual void copy_heap(int partition, MultiCacheHeapGC * gc)
00393 {
00394 (void) partition;
00395 (void) gc;
00396 }
00397
00398
00399
00400
00401 void alloc_mutexes()
00402 {
00403 for (int i = 0; i < MULTI_CACHE_PARTITIONS; i++)
00404 locks[i] = new_ProxyMutex();
00405 }
00406 PtrMutex locks[MULTI_CACHE_PARTITIONS];
00407 };
00408
00409 template<class C> struct MultiCache: public MultiCacheBase
00410 {
00411 int get_elementsize()
00412 {
00413 return sizeof(C);
00414 }
00415
00416 MultiCacheBase *dup()
00417 {
00418 return new MultiCache<C>;
00419 }
00420
00421 void rebuild_element(int buck, char *elem, RebuildMC & r);
00422
00423 virtual int rebuild_callout(C * c, RebuildMC & r)
00424 {
00425 (void) c;
00426 (void) r;
00427 return 1;
00428 }
00429
00430 virtual void rebuild_insert_callout(C * c, RebuildMC & r)
00431 {
00432 (void) c;
00433 (void) r;
00434 }
00435
00436
00437
00438
00439 int level_of_block(C * b);
00440 bool match(uint64_t folded_md5, C * block);
00441 C *cache_bucket(uint64_t folded_md5, int level);
00442 C *insert_block(uint64_t folded_md5, C * new_block, int level);
00443 void flush(C * b, int bucket, int level);
00444 void delete_block(C * block);
00445 C *lookup_block(uint64_t folded_md5, int level);
00446 void copy_heap(int paritition, MultiCacheHeapGC *);
00447 };
00448
00449 inline uint64_t
00450 fold_md5(INK_MD5 const& md5)
00451 {
00452 return md5.fold();
00453 }
00454
00455 template<class C> inline int MultiCache<C>::level_of_block(C * b)
00456 {
00457 if ((char *) b - data >= level_offset[1]) {
00458 if ((char *) b - data >= level_offset[2])
00459 return 2;
00460 return 1;
00461 }
00462 return 0;
00463 }
00464
00465 template<class C> inline C * MultiCache<C>::cache_bucket(uint64_t folded_md5, int level)
00466 {
00467 int bucket = (int) (folded_md5 % buckets);
00468 char *offset = data + level_offset[level] + bucketsize[level] * bucket;
00469 return (C *) offset;
00470 }
00471
00472
00473
00474
00475 template<class C> inline C * MultiCache<C>::insert_block(uint64_t folded_md5, C * new_block, int level)
00476 {
00477 C *b = cache_bucket(folded_md5, level);
00478 C *block = NULL, *empty = NULL;
00479 int bucket = (int) (folded_md5 % buckets);
00480 int hits = 0;
00481
00482
00483
00484 uint64_t tag = make_tag(folded_md5);
00485 int n_empty = 0;
00486
00487 for (block = b; block < b + elements[level]; block++) {
00488 if (block->is_empty() && !empty) {
00489 n_empty++;
00490 empty = block;
00491 }
00492 if (tag == block->tag())
00493 goto Lfound;
00494 hits += block->hits;
00495 }
00496 if (empty) {
00497 block = empty;
00498 goto Lfound;
00499 }
00500
00501 {
00502 C *best = NULL;
00503 int again = 1;
00504 do {
00505
00506
00507
00508 unsigned int dec = 0;
00509 if (hits > ((max_hits / 2) + 1) * elements[level])
00510 dec = 1;
00511 for (block = b; block < b + elements[level]; block++) {
00512 if (block->backed && (!best || best->hits > block->hits))
00513 best = block;
00514 if (block->hits)
00515 block->hits -= dec;
00516 }
00517 if (best) {
00518 block = best;
00519 goto Lfound;
00520 }
00521 flush(b, bucket, level);
00522 } while (again--);
00523 ink_assert(!"cache flush failure");
00524 }
00525
00526 Lfound:
00527 if (new_block) {
00528 *block = *new_block;
00529 int *hop = new_block->heap_offset_ptr();
00530 if (hop)
00531 update(block->heap_offset_ptr(), hop);
00532 block->backed = 0;
00533 } else
00534 block->reset();
00535 block->set_full(folded_md5, buckets);
00536 ink_assert(block->tag() == tag);
00537 return block;
00538 }
00539
00540 #define REBUILD_FOLDED_MD5(_cl) \
00541 ((_cl->tag() * (uint64_t)buckets + (uint64_t)bucket))
00542
00543
00544
00545
00546 template<class C> inline void MultiCache<C>::flush(C * b, int bucket, int level)
00547 {
00548 C *block = NULL;
00549 if (level < levels - 1) {
00550 if (level >= lowest_level(bucket))
00551 set_lowest_level(bucket, level + 1);
00552 for (block = b; block < b + elements[level]; block++) {
00553 ink_assert(!block->is_empty());
00554 insert_block(REBUILD_FOLDED_MD5(block), block, level + 1);
00555 block->backed = true;
00556 }
00557 } else {
00558 for (block = b; block < b + elements[level]; block++)
00559 if (!block->is_empty())
00560 block->backed = true;
00561 }
00562 }
00563
00564
00565
00566
00567 template<class C> inline bool MultiCache<C>::match(uint64_t folded_md5, C * block)
00568 {
00569 return block->tag() == make_tag(folded_md5);
00570 }
00571
00572
00573
00574
00575 template<class C> inline void MultiCache<C>::delete_block(C * b)
00576 {
00577 if (b->backed) {
00578 int l = level_of_block(b);
00579 if (l < levels - 1) {
00580 int bucket = (((char *) b - data) - level_offset[l]) / bucketsize[l];
00581 C *x = (C *) (data + level_offset[l + 1] + bucket * bucketsize[l + 1]);
00582 for (C * y = x; y < x + elements[l + 1]; y++)
00583 if (b->tag() == y->tag())
00584 delete_block(y);
00585 }
00586 }
00587 b->set_empty();
00588 }
00589
00590
00591
00592
00593 template<class C> inline C * MultiCache<C>::lookup_block(uint64_t folded_md5, int level)
00594 {
00595 C *b = cache_bucket(folded_md5, 0);
00596 uint64_t tag = make_tag(folded_md5);
00597 int i = 0;
00598
00599 for (i = 0; i < elements[0]; i++)
00600 if (tag == b[i].tag())
00601 return &b[i];
00602 if (level <= 0)
00603 return NULL;
00604
00605 b = cache_bucket(folded_md5, 1);
00606 for (i = 0; i < elements[1]; i++)
00607 if (tag == b[i].tag())
00608 return &b[i];
00609 if (level <= 1)
00610 return NULL;
00611
00612 b = cache_bucket(folded_md5, 2);
00613 for (i = 0; i < elements[2]; i++)
00614 if (tag == b[i].tag())
00615 return &b[i];
00616 return NULL;
00617 }
00618
00619 template<class C> inline void MultiCache<C>::rebuild_element(int bucket, char *elem, RebuildMC & r)
00620 {
00621 C *e = (C *) elem;
00622 if (!e->is_empty()) {
00623 r.total++;
00624 if (e->is_deleted())
00625 r.deleted++;
00626 if (e->backed)
00627 r.backed++;
00628 int res = rebuild_callout(e, r);
00629 if (res < 0)
00630 r.corrupt++;
00631 else if (!res)
00632 r.stale++;
00633 else {
00634 r.good++;
00635 if (lookup_block(REBUILD_FOLDED_MD5(e), levels - 1))
00636 if (!e->backed)
00637 r.duplicates++;
00638 C *new_e = insert_block(REBUILD_FOLDED_MD5(e), e, 0);
00639 rebuild_insert_callout(new_e, r);
00640 }
00641 }
00642 }
00643
00644 template<class C> inline void MultiCache<C>::copy_heap(int partition, MultiCacheHeapGC * gc)
00645 {
00646 int b = first_bucket_of_partition(partition);
00647 int n = buckets_of_partition(partition);
00648 for (int level = 0; level < levels; level++) {
00649 int e = n * elements[level];
00650 char *d = data + level_offset[level] + b * bucketsize[level];
00651 C *x = (C *) d;
00652 for (int i = 0; i < e; i++) {
00653 int s = x[i].heap_size();
00654 if (s) {
00655 int *pi = x[i].heap_offset_ptr();
00656 if (pi) {
00657 char *src = (char *) ptr(pi, partition);
00658 if (src) {
00659 if (heap_halfspace) {
00660 if (src >= heap + halfspace_size())
00661 continue;
00662 } else if (src < heap + halfspace_size())
00663 continue;
00664 copy_heap_data(src, s, pi, partition, gc);
00665 }
00666 }
00667 }
00668 }
00669 }
00670 }
00671
00672
00673 void stealStore(Store & s, int blocks);
00674 #endif