• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

P_MultiCache.h

Go to the documentation of this file.
00001 /** @file
00002 
00003   A brief file description
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
00022  */
00023 
00024 /****************************************************************************
00025 
00026   MultiCache.h
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 // Constants
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 // for heap_offset() and heap_size(), indicates no data
00051 #define MULTI_CACHE_HEAP_NONE       -1
00052 
00053 #define MULTI_CACHE_MAGIC_NUMBER     0x0BAD2D8
00054 
00055 // Update these if there is a change to MultiCacheBase
00056 // There is a separate HOST_DB_CACHE_[MAJOR|MINOR]_VERSION
00057 #define MULTI_CACHE_MAJOR_VERSION    2
00058 #define MULTI_CACHE_MINOR_VERSION    1
00059 // 2.1 - IPv6 compatible
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 // unused.. possible optimization
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 // Types
00076 //
00077 
00078 // MultiCacheBlock
00079 // This is an abstract class which simply documents the operations
00080 // required by the templated cache operations.
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   // optional heap
00141   int heap_size;
00142   volatile int heap_halfspace;
00143   volatile int heap_used[2];
00144 
00145     MultiCacheHeader();
00146 };
00147 
00148 // size of block of unsunk pointers with respect to the number of
00149 // elements
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;                 // doubles as freelist pointer
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 // Broken SunCC
00179 //
00180 #define PtrMutex Ptr<ProxyMutex>
00181 
00182 //
00183 // used by windows only - to keep track
00184 // of mapping handles
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   // mmap-ed region
00206   //
00207   char *data;
00208   char *lowest_level_data;
00209 
00210   // equal to data + level_offset[3] + bucketsize[3] * buckets;
00211   char *heap;
00212 
00213   // interface functions
00214   //
00215   int halfspace_size()
00216   {
00217     return heap_size / 2;
00218   }
00219 
00220   // Stats support
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   // Fixed point, 8 bits shifted left
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   // 1 for success, 0 for no config file, -1 for failure
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();                 // this zeros the data
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   // Rebuild the database, also perform checks, and fixups
00295   // ** cannot be called on a running system **
00296   // "this" must be initialized.
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);     // 0 on success
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   // Check the database
00313   // ** cannot be called on a running system **
00314   //  assumes that the configuration is correct
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     // beeping gcc 2.7.2 is broken
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);      // part varies between 0 and MULTI_CACHE_PARTITIONS
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   // Heap support
00358   UnsunkPtrRegistry unsunk[MULTI_CACHE_PARTITIONS];
00359 
00360   // -1 on error
00361   int ptr_to_partition(char *);
00362   // the user must pass in the offset field within the
00363   // MultiCacheBlock object.  The offset will be inserted
00364   // into the object on success and a pointer to the data
00365   // returned.  On failure, NULL is returned;
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   // Private
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];       // 1 lock per (buckets/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   // -1 is corrupt, 0 == void (do not insert), 1 is OK
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   // template operations
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 // Insert an entry
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   // Find the entry
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       // Find an entry previously backed to a higher level.
00506       // self scale the hits number within the bucket
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 // This function ejects some number of entries.
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 // Match a cache line and a folded md5 key
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 // This code is a bit of a mess and should probably be rewritten
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 // Lookup an entry up to some level in the cache
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   // Level 0
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   // Level 1
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   // Level 2
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 // store either free or in the cache, can be stolen for reconfiguration
00673 void stealStore(Store & s, int blocks);
00674 #endif /* _MultiCache_h_ */

Generated by  doxygen 1.7.1