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

MultiCache.cc

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.cc
00027 
00028 
00029  ****************************************************************************/
00030 
00031 #include "libts.h"
00032 #include "I_Layout.h"
00033 #include "P_HostDB.h"
00034 #include "P_MultiCache.h"
00035 #include "P_EventSystem.h"      // FIXME: need to have this in I_* header files.
00036 #include "ink_file.h"
00037 
00038 static const int MC_SYNC_MIN_PAUSE_TIME = HRTIME_MSECONDS(200); // Pause for at least 200ms
00039 
00040 MultiCacheBase::MultiCacheBase()
00041   : store(0), mapped_header(NULL), data(0), lowest_level_data(0), miss_stat(0), buckets_per_partitionF8(0)
00042 {
00043   filename[0] = 0;
00044   memset(hit_stat, 0, sizeof(hit_stat));
00045   memset(unsunk, 0, sizeof(unsunk));
00046   for (int i = 0; i < MULTI_CACHE_PARTITIONS; i++)
00047     unsunk[i].mc = this;
00048 }
00049 
00050 inline int
00051 store_verify(Store *store)
00052 {
00053   if (!store)
00054     return 0;
00055   for (unsigned i = 0; i < store->n_disks; i++) {
00056     for (Span * sd = store->disk[i]; sd; sd = sd->link.next) {
00057       if (!sd->file_pathname && sd->offset)
00058         return 0;
00059     }
00060   }
00061   return 1;
00062 }
00063 
00064 MultiCacheHeader::MultiCacheHeader()
00065   : magic(MULTI_CACHE_MAGIC_NUMBER), levels(0),
00066     tag_bits(0), max_hits(0), elementsize(0),
00067     buckets(0), totalelements(0), totalsize(0), nominal_elements(0), heap_size(0), heap_halfspace(0)
00068 {
00069   memset(level_offset, 0, sizeof(level_offset));
00070   memset(bucketsize, 0, sizeof(bucketsize));
00071   memset(elements, 0, sizeof(elements));
00072   heap_used[0] = 8;
00073   heap_used[1] = 8;
00074   version.ink_major = MULTI_CACHE_MAJOR_VERSION;
00075   version.ink_minor = MULTI_CACHE_MINOR_VERSION;
00076 }
00077 
00078 static inline int
00079 bytes_to_blocks(int64_t b)
00080 {
00081   return (int) ((b + (STORE_BLOCK_SIZE - 1)) / STORE_BLOCK_SIZE);
00082 }
00083 
00084 inline int
00085 MultiCacheBase::blocks_in_level(int level)
00086 {
00087   int64_t sumbytes = 0;
00088   int prevblocks = 0;
00089   int b = 0;
00090   for (int i = 0; i <= level; i++) {
00091     sumbytes += buckets * ((int64_t) bucketsize[i]);
00092     int sumblocks = bytes_to_blocks(sumbytes);
00093     b = sumblocks - prevblocks;
00094     prevblocks = sumblocks;
00095   }
00096   return b;
00097 }
00098 
00099 //
00100 // Initialize MultiCache
00101 // The outermost level of the cache contains ~aelements.
00102 // The higher levels (lower in number) contain fewer.
00103 //
00104 int
00105 MultiCacheBase::initialize(Store *astore, char *afilename,
00106                            int aelements, int abuckets, int alevels,
00107                            int level0_elements_per_bucket,
00108                            int level1_elements_per_bucket, int level2_elements_per_bucket)
00109 {
00110   int64_t size = 0;
00111 
00112   Debug("multicache", "initializing %s with %d elements, %d buckets and %d levels", afilename, aelements, abuckets, alevels);
00113   ink_assert(alevels < 4);
00114   levels = alevels;
00115   elementsize = get_elementsize();
00116   totalelements = 0;
00117   nominal_elements = aelements;
00118   buckets = abuckets;
00119 
00120   ink_strlcpy(filename, afilename, sizeof(filename));
00121   //
00122   //  Allocate level 2 as the outermost
00123   //
00124   if (levels > 2) {
00125     if (!buckets) {
00126       buckets = aelements / level2_elements_per_bucket;
00127       if (buckets < MULTI_CACHE_PARTITIONS)
00128         buckets = MULTI_CACHE_PARTITIONS;
00129     }
00130     if (levels == 3)
00131       level2_elements_per_bucket = aelements / buckets;
00132     elements[2] = level2_elements_per_bucket;
00133     totalelements += buckets * level2_elements_per_bucket;
00134     bucketsize[2] = elementsize * level2_elements_per_bucket;
00135     size += (int64_t) bucketsize[2] * (int64_t) buckets;
00136 
00137     if (!(level2_elements_per_bucket / level1_elements_per_bucket)) {
00138       Warning("Size change too large, unable to reconfigure");
00139       return -1;
00140     }
00141 
00142     aelements /= (level2_elements_per_bucket / level1_elements_per_bucket);
00143   }
00144   //
00145   //  Allocate level 1
00146   //
00147   if (levels > 1) {
00148     if (!buckets) {
00149       buckets = aelements / level1_elements_per_bucket;
00150       if (buckets < MULTI_CACHE_PARTITIONS)
00151         buckets = MULTI_CACHE_PARTITIONS;
00152     }
00153     if (levels == 2)
00154       level1_elements_per_bucket = aelements / buckets;
00155     elements[1] = level1_elements_per_bucket;
00156     totalelements += buckets * level1_elements_per_bucket;
00157     bucketsize[1] = elementsize * level1_elements_per_bucket;
00158     size += (int64_t) bucketsize[1] * (int64_t) buckets;
00159     if (!(level1_elements_per_bucket / level0_elements_per_bucket)) {
00160       Warning("Size change too large, unable to reconfigure");
00161       return -2;
00162     }
00163     aelements /= (level1_elements_per_bucket / level0_elements_per_bucket);
00164   }
00165   //
00166   //  Allocate level 0
00167   //
00168   if (!buckets) {
00169     buckets = aelements / level0_elements_per_bucket;
00170     if (buckets < MULTI_CACHE_PARTITIONS)
00171       buckets = MULTI_CACHE_PARTITIONS;
00172   }
00173   if (levels == 1)
00174     level0_elements_per_bucket = aelements / buckets;
00175   elements[0] = level0_elements_per_bucket;
00176   totalelements += buckets * level0_elements_per_bucket;
00177   bucketsize[0] = elementsize * level0_elements_per_bucket;
00178   size += (int64_t) bucketsize[0] * (int64_t) buckets;
00179 
00180   buckets_per_partitionF8 = (buckets << 8) / MULTI_CACHE_PARTITIONS;
00181   ink_release_assert(buckets_per_partitionF8);
00182 
00183   unsigned int blocks = (size + (STORE_BLOCK_SIZE - 1)) / STORE_BLOCK_SIZE;
00184 
00185   heap_size = int ((float)totalelements * estimated_heap_bytes_per_entry());
00186   blocks += bytes_to_blocks(heap_size);
00187 
00188   blocks += 1;                  // header
00189   totalsize = (int64_t) blocks *(int64_t) STORE_BLOCK_SIZE;
00190 
00191   Debug("multicache", "heap_size = %d, totalelements = %d, totalsize = %d", heap_size, totalelements, totalsize);
00192 
00193   //
00194   //  Spread alloc from the store (using storage that can be mmapped)
00195   //
00196   delete store;
00197   store = new Store;
00198   astore->spread_alloc(*store, blocks, true);
00199   unsigned int got = store->total_blocks();
00200 
00201   if (got < blocks) {
00202     astore->free(*store);
00203     delete store;
00204     store = NULL;
00205     Warning("Configured store too small, unable to reconfigure");
00206     return -3;
00207   }
00208   totalsize = (STORE_BLOCK_SIZE) * blocks;
00209 
00210   level_offset[1] = buckets * bucketsize[0];
00211   level_offset[2] = buckets * bucketsize[1] + level_offset[1];
00212 
00213   if (lowest_level_data)
00214     delete[]lowest_level_data;
00215   lowest_level_data = new char[lowest_level_data_size()];
00216   ink_assert(lowest_level_data);
00217   memset(lowest_level_data, 0xFF, lowest_level_data_size());
00218 
00219   return got;
00220 }
00221 
00222 char *
00223 MultiCacheBase::mmap_region(int blocks, int *fds, char *cur, size_t& total_length, bool private_flag, int zero_fill)
00224 {
00225   if (!blocks)
00226     return cur;
00227   int p = 0;
00228   char *res = 0;
00229   for (unsigned i = 0; i < store->n_disks; i++) {
00230     unsigned int target = blocks / (store->n_disks - i);
00231     unsigned int following = store->total_blocks(i + 1);
00232     if (blocks - target > following)
00233       target = blocks - following;
00234     Span *ds = store->disk[i];
00235     for (int j = 0; j < store->disk[i]->paths(); j++) {
00236       Span *d = ds->nth(j);
00237 
00238       ink_assert(d->is_mmapable());
00239 
00240       if (target && d->blocks) {
00241         int b = d->blocks;
00242         if (d->blocks > target)
00243           b = target;
00244         d->blocks -= b;
00245         unsigned int nbytes = b * STORE_BLOCK_SIZE;
00246         int fd = fds[p] ? fds[p] : zero_fill;
00247         ink_assert(-1 != fd);
00248         int flags = private_flag ? MAP_PRIVATE : MAP_SHARED_MAP_NORESERVE;
00249 
00250         if (cur)
00251           res = (char *) mmap(cur, nbytes, PROT_READ | PROT_WRITE, MAP_FIXED | flags, fd, d->offset * STORE_BLOCK_SIZE);
00252         else
00253           res = (char *) mmap(cur, nbytes, PROT_READ | PROT_WRITE, flags, fd, d->offset * STORE_BLOCK_SIZE);
00254 
00255         d->offset += b;
00256 
00257         if (res == NULL || res == (caddr_t) MAP_FAILED)
00258           return NULL;
00259         ink_assert(!cur || res == cur);
00260         cur = res + nbytes;
00261         blocks -= b;
00262         total_length += nbytes; // total amount mapped.
00263       }
00264       p++;
00265     }
00266   }
00267   return blocks ? 0 : cur;
00268 }
00269 
00270 void
00271 MultiCacheBase::reset()
00272 {
00273   if (store)
00274     delete store;
00275   store = 0;
00276   if (lowest_level_data)
00277     delete[]lowest_level_data;
00278   lowest_level_data = 0;
00279   if (data)
00280     unmap_data();
00281   data = 0;
00282 }
00283 
00284 int
00285 MultiCacheBase::unmap_data()
00286 {
00287   int res = 0;
00288   if (data) {
00289     res = munmap(data, totalsize);
00290     data = NULL;
00291     return res;
00292   }
00293   return 0;
00294 }
00295 
00296 int
00297 MultiCacheBase::mmap_data(bool private_flag, bool zero_fill)
00298 {
00299   ats_scoped_fd fd;
00300   int fds[MULTI_CACHE_MAX_FILES] = { 0 };
00301   int n_fds = 0;
00302   size_t total_mapped = 0; // total mapped memory from storage.
00303 
00304   // open files
00305   //
00306   if (!store || !store->n_disks)
00307     goto Lalloc;
00308   for (unsigned i = 0; i < store->n_disks; i++) {
00309     Span *ds = store->disk[i];
00310     for (int j = 0; j < store->disk[i]->paths(); j++) {
00311       char path[PATH_NAME_MAX + 1];
00312       Span *d = ds->nth(j);
00313       int r = d->path(filename, NULL, path, PATH_NAME_MAX);
00314       if (r < 0) {
00315         Warning("filename too large '%s'", filename);
00316         goto Labort;
00317       }
00318       fds[n_fds] = socketManager.open(path, O_RDWR | O_CREAT, 0644);
00319       if (fds[n_fds] < 0) {
00320         if (!zero_fill) {
00321           Warning("unable to open file '%s': %d, %s", path, errno, strerror(errno));
00322           goto Lalloc;
00323         }
00324         fds[n_fds] = 0;
00325       }
00326       if (!d->file_pathname) {
00327         struct stat fd_stat;
00328 
00329         if (fstat(fds[n_fds], &fd_stat) < 0) {
00330           Warning("unable to stat file '%s'", path);
00331           goto Lalloc;
00332         } else {
00333           int64_t size = (off_t)(d->blocks * STORE_BLOCK_SIZE);
00334 
00335           if (fd_stat.st_size != size) {
00336             int err = ink_file_fd_zerofill(fds[n_fds], size);
00337 
00338             if (err != 0) {
00339               Warning("unable to set file '%s' size to %" PRId64 ": %d, %s", path, size, err, strerror(err));
00340               goto Lalloc;
00341             }
00342           }
00343         }
00344       }
00345       n_fds++;
00346     }
00347   }
00348 
00349   data = 0;
00350 
00351 
00352   // mmap levels
00353   //
00354   {
00355     // make a copy of the store
00356     Store tStore;
00357     store->dup(tStore);
00358     Store *saved = store;
00359     store = &tStore;
00360 
00361     char *cur = 0;
00362 
00363 // find a good address to start
00364 #if !defined(darwin)
00365     fd = socketManager.open("/dev/zero", O_RDONLY, 0645);
00366     if (fd < 0) {
00367       store = saved;
00368       Warning("unable to open /dev/zero: %d, %s", errno, strerror(errno));
00369       goto Labort;
00370     }
00371 #endif
00372 
00373     // lots of useless stuff
00374 #if defined(darwin)
00375     cur = (char *) mmap(0, totalsize, PROT_READ, MAP_SHARED_MAP_NORESERVE | MAP_ANON, -1, 0);
00376 #else
00377     cur = (char *) mmap(0, totalsize, PROT_READ, MAP_SHARED_MAP_NORESERVE, fd, 0);
00378 #endif
00379     if (cur == NULL || cur == (caddr_t) MAP_FAILED) {
00380       store = saved;
00381 #if defined(darwin)
00382       Warning("unable to mmap anonymous region for %u bytes: %d, %s", totalsize, errno, strerror(errno));
00383 #else
00384       Warning("unable to mmap /dev/zero for %u bytes: %d, %s", totalsize, errno, strerror(errno));
00385 #endif
00386       goto Labort;
00387     }
00388     if (munmap(cur, totalsize)) {
00389       store = saved;
00390 #if defined(darwin)
00391       Warning("unable to munmap anonymous region for %u bytes: %d, %s", totalsize, errno, strerror(errno));
00392 #else
00393       Warning("unable to munmap /dev/zero for %u bytes: %d, %s", totalsize, errno, strerror(errno));
00394 #endif
00395       goto Labort;
00396     }
00397 
00398     /* We've done a mmap on a target region of the maximize size we need. Now we drop that mapping
00399        and do the real one, keeping at the same address space (stored in @a data) which should work because
00400        we just tested it.
00401     */
00402     // coverity[use_after_free]
00403     data = cur;
00404 
00405     cur = mmap_region(blocks_in_level(0), fds, cur, total_mapped, private_flag, fd);
00406     if (!cur) {
00407       store = saved;
00408       goto Labort;
00409     }
00410     if (levels > 1)
00411       cur = mmap_region(blocks_in_level(1), fds, cur, total_mapped, private_flag, fd);
00412     if (!cur) {
00413       store = saved;
00414       goto Labort;
00415     }
00416     if (levels > 2)
00417       cur = mmap_region(blocks_in_level(2), fds, cur, total_mapped, private_flag, fd);
00418     if (!cur) {
00419       store = saved;
00420       goto Labort;
00421     }
00422 
00423     if (heap_size) {
00424       heap = cur;
00425       cur = mmap_region(bytes_to_blocks(heap_size), fds, cur, total_mapped, private_flag, fd);
00426       if (!cur) {
00427         store = saved;
00428         goto Labort;
00429       }
00430     }
00431     mapped_header = (MultiCacheHeader *) cur;
00432     if (!mmap_region(1, fds, cur, total_mapped, private_flag, fd)) {
00433       store = saved;
00434       goto Labort;
00435     }
00436 #if !defined(darwin)
00437     ink_assert(!socketManager.close(fd));
00438 #endif
00439     store = saved;
00440   }
00441 
00442 
00443   for (int i = 0; i < n_fds; i++) {
00444     if (fds[i] >= 0)
00445       ink_assert(!socketManager.close(fds[i]));
00446   }
00447 
00448   return 0;
00449 Lalloc:
00450   {
00451     free(data);
00452     char *cur = 0;
00453 
00454     data = (char *)ats_memalign(ats_pagesize(), totalsize);
00455     cur = data + STORE_BLOCK_SIZE * blocks_in_level(0);
00456     if (levels > 1)
00457       cur = data + STORE_BLOCK_SIZE * blocks_in_level(1);
00458     if (levels > 2)
00459       cur = data + STORE_BLOCK_SIZE * blocks_in_level(2);
00460     if (heap_size) {
00461       heap = cur;
00462       cur += bytes_to_blocks(heap_size) * STORE_BLOCK_SIZE;
00463     }
00464     mapped_header = (MultiCacheHeader *) cur;
00465     for (int i = 0; i < n_fds; i++) {
00466       if (fds[i] >= 0)
00467         socketManager.close(fds[i]);
00468     }
00469 
00470     return 0;
00471   }
00472 
00473 Labort:
00474   for (int i = 0; i < n_fds; i++) {
00475     if (fds[i] >= 0)
00476       socketManager.close(fds[i]);
00477   }
00478   if (total_mapped > 0)
00479     munmap(data, total_mapped);
00480 
00481   return -1;
00482 }
00483 
00484 void
00485 MultiCacheBase::clear()
00486 {
00487   memset(data, 0, totalsize);
00488   heap_used[0] = 8;
00489   heap_used[1] = 8;
00490   heap_halfspace = 0;
00491   *mapped_header = *(MultiCacheHeader *) this;
00492 }
00493 
00494 void
00495 MultiCacheBase::clear_but_heap()
00496 {
00497   memset(data, 0, totalelements * elementsize);
00498   *mapped_header = *(MultiCacheHeader *) this;
00499 }
00500 
00501 int
00502 MultiCacheBase::read_config(const char *config_filename, Store & s, char *fn, int *pi, int *pbuck)
00503 {
00504   int scratch;
00505   ats_scoped_str rundir(RecConfigReadRuntimeDir());
00506   char p[PATH_NAME_MAX + 1], buf[256];
00507 
00508   Layout::relative_to(p, sizeof(p), rundir, config_filename);
00509 
00510   ats_scoped_fd fd(::open(p, O_RDONLY));
00511   if (fd < 0)
00512     return 0;
00513 
00514   if (ink_file_fd_readline(fd, sizeof(buf), buf) <= 0)
00515     return -1;
00516   // coverity[secure_coding]
00517   if (sscanf(buf, "%d\n", pi ? pi : &scratch) != 1)
00518     return -1;
00519 
00520   if (ink_file_fd_readline(fd, sizeof(buf), buf) <= 0)
00521     return -1;
00522   // coverity[secure_coding]
00523   if (sscanf(buf, "%d\n", pbuck ? pbuck : &scratch) != 1)
00524     return -1;
00525 
00526   if (ink_file_fd_readline(fd, sizeof(buf), buf) <= 0)
00527     return -1;
00528   // coverity[secure_coding]
00529   if (sscanf(buf, "%d\n", &heap_size) != 1)
00530     return -1;
00531 
00532   if (s.read(fd, fn) < 0)
00533     return -1;
00534 
00535   return 1;
00536 }
00537 
00538 int
00539 MultiCacheBase::write_config(const char *config_filename, int nominal_size, int abuckets)
00540 {
00541   ats_scoped_str rundir(RecConfigReadRuntimeDir());
00542   char p[PATH_NAME_MAX + 1], buf[256];
00543   int fd, retcode = -1;
00544 
00545   Layout::relative_to(p, sizeof(p), rundir, config_filename);
00546 
00547   // XXX: Shouldn't that be 0664?
00548   //
00549   if ((fd =::open(p, O_CREAT | O_WRONLY | O_TRUNC, 0666)) >= 0) {
00550     snprintf(buf, sizeof(buf) - 1, "%d\n%d\n%d\n", nominal_size, abuckets, heap_size);
00551     buf[sizeof(buf) - 1] = 0;
00552     if (ink_file_fd_writestring(fd, buf) != -1 && store->write(fd, filename) >= 0)
00553       retcode = 0;
00554     ::close(fd);
00555   } else
00556     Warning("unable to open '%s' for write: %d, %s", p, errno, strerror(errno));
00557 
00558   return retcode;
00559 }
00560 
00561 int
00562 MultiCacheBase::open(Store *s, const char *config_filename, char *db_filename, int db_size,
00563                      bool reconfigure, bool fix, bool silent)
00564 {
00565   int ret = 0;
00566   const char *err = NULL;
00567   char *serr = NULL;
00568   char t_db_filename[PATH_NAME_MAX];
00569   int t_db_size = 0;
00570   int t_db_buckets = 0;
00571   int change = 0;
00572 
00573   t_db_filename[0] = 0;
00574 
00575   // Set up cache
00576   {
00577     Store tStore;
00578     int res = read_config(config_filename, tStore, t_db_filename, &t_db_size, &t_db_buckets);
00579 
00580     ink_assert(store_verify(&tStore));
00581     if (res < 0)
00582       goto LfailRead;
00583     if (!res) {
00584       if (!reconfigure || !db_filename || !db_size)
00585         goto LfailConfig;
00586       if (initialize(s, db_filename, db_size) <= 0)
00587         goto LfailInit;
00588       write_config(config_filename, db_size, buckets);
00589       if (mmap_data() < 0)
00590         goto LfailMap;
00591       clear();
00592     } else {
00593       // don't know how to rebuild from this problem
00594       ink_assert(!db_filename || !strcmp(t_db_filename, db_filename));
00595       if (!db_filename)
00596         db_filename = t_db_filename;
00597 
00598       // Has the size changed?
00599       change = (db_size >= 0) ? (db_size - t_db_size) : 0;
00600       if (db_size < 0)
00601         db_size = t_db_size;
00602       if (change && !reconfigure)
00603         goto LfailConfig;
00604 
00605       Store cStore;
00606       tStore.dup(cStore);
00607 
00608       // Try to get back our storage
00609       Store diff;
00610 
00611       s->try_realloc(cStore, diff);
00612       if (diff.n_disks && !reconfigure)
00613         goto LfailConfig;
00614 
00615       // Do we need to do a reconfigure?
00616       if (diff.n_disks || change) {
00617         // find a new store to old the amount of space we need
00618         int delta = change;
00619 
00620         if (diff.n_disks)
00621           delta += diff.total_blocks();
00622 
00623         if (delta) {
00624           if (delta > 0) {
00625             Store freeStore;
00626             stealStore(freeStore, delta);
00627             Store more;
00628             freeStore.spread_alloc(more, delta);
00629             if (delta > (int) more.total_blocks())
00630               goto LfailReconfig;
00631             Store more_diff;
00632             s->try_realloc(more, more_diff);
00633             if (more_diff.n_disks)
00634               goto LfailReconfig;
00635             cStore.add(more);
00636             if (more.clear(db_filename, false) < 0)
00637               goto LfailReconfig;
00638           }
00639           if (delta < 0) {
00640             Store removed;
00641             cStore.spread_alloc(removed, -delta);
00642           }
00643         }
00644         cStore.sort();
00645         if (initialize(&cStore, db_filename, db_size, t_db_buckets) <= 0)
00646           goto LfailInit;
00647 
00648         ink_assert(store_verify(store));
00649 
00650         if (write_config(config_filename, db_size, buckets) < 0)
00651           goto LfailWrite;
00652 
00653         ink_assert(store_verify(store));
00654 
00655         //  rebuild
00656         MultiCacheBase *old = dup();
00657         if (old->initialize(&tStore, t_db_filename, t_db_size, t_db_buckets) <= 0) {
00658           delete old;
00659           goto LfailInit;
00660         }
00661 
00662         if (rebuild(*old)) {
00663           delete old;
00664           goto LfailRebuild;
00665         }
00666         ink_assert(store_verify(store));
00667         delete old;
00668 
00669       } else {
00670         if (initialize(&tStore, db_filename, db_size, t_db_buckets) <= 0)
00671           goto LfailFix;
00672         ink_assert(store_verify(store));
00673         if (mmap_data() < 0)
00674           goto LfailMap;
00675         if (!verify_header())
00676           goto LheaderCorrupt;
00677         *(MultiCacheHeader *) this = *mapped_header;
00678         ink_assert(store_verify(store));
00679 
00680         if (fix)
00681           if (check(config_filename, true) < 0)
00682             goto LfailFix;
00683       }
00684     }
00685   }
00686 
00687   if (store)
00688     ink_assert(store_verify(store));
00689 Lcontinue:
00690   return ret;
00691 
00692 LheaderCorrupt:
00693   err = "header missing/corrupt";
00694   goto Lfail;
00695 
00696 LfailWrite:
00697   err = "unable to write";
00698   serr = strerror(errno);
00699   goto Lfail;
00700 
00701 LfailRead:
00702   err = "unable to read";
00703   serr = strerror(errno);
00704   goto Lfail;
00705 
00706 LfailInit:
00707   err = "unable to initialize database (too little storage)\n";
00708   goto Lfail;
00709 
00710 LfailConfig:
00711   err = "configuration changed";
00712   goto Lfail;
00713 
00714 LfailReconfig:
00715   err = "unable to reconfigure";
00716   goto Lfail;
00717 
00718 LfailRebuild:
00719   err = "unable to rebuild";
00720   goto Lfail;
00721 
00722 LfailFix:
00723   err = "unable to fix";
00724   goto Lfail;
00725 
00726 LfailMap:
00727   err = "unable to mmap";
00728   serr = strerror(errno);
00729   goto Lfail;
00730 
00731 Lfail:
00732   {
00733     unmap_data();
00734     if (!silent) {
00735       if (reconfigure) {
00736         RecSignalWarning(REC_SIGNAL_CONFIG_ERROR, "%s: [%s] %s: disabling database\n"
00737                      "You may need to 'reconfigure' your cache manually.  Please refer to\n"
00738                      "the 'Configuration' chapter in the manual.", err, config_filename, serr ? serr : "");
00739       } else {
00740         RecSignalWarning(REC_SIGNAL_CONFIG_ERROR, "%s: [%s] %s: reinitializing database", err, config_filename,
00741                      serr ? serr : "");
00742       }
00743     }
00744   }
00745   ret = -1;
00746   goto Lcontinue;
00747 }
00748 
00749 bool
00750 MultiCacheBase::verify_header()
00751 {
00752   return
00753     mapped_header->magic == magic &&
00754     mapped_header->version.ink_major == version.ink_major &&
00755     mapped_header->version.ink_minor == version.ink_minor &&
00756     mapped_header->levels == levels &&
00757     mapped_header->tag_bits == tag_bits &&
00758     mapped_header->max_hits == max_hits &&
00759     mapped_header->elementsize == elementsize &&
00760     mapped_header->buckets == buckets &&
00761     mapped_header->level_offset[0] == level_offset[0] &&
00762     mapped_header->level_offset[1] == level_offset[1] &&
00763     mapped_header->level_offset[2] == level_offset[2] &&
00764     mapped_header->elements[0] == elements[0] &&
00765     mapped_header->elements[1] == elements[1] &&
00766     mapped_header->elements[2] == elements[2] &&
00767     mapped_header->bucketsize[0] == bucketsize[0] &&
00768     mapped_header->bucketsize[1] == bucketsize[1] &&
00769     mapped_header->bucketsize[2] == bucketsize[2] &&
00770     mapped_header->totalelements == totalelements &&
00771     mapped_header->totalsize == totalsize && mapped_header->nominal_elements == nominal_elements;
00772 }
00773 
00774 void
00775 MultiCacheBase::print_info(FILE *fp)
00776 {                               // STDIO OK
00777   fprintf(fp, "    Elements:       %-10d\n", totalelements);
00778   fprintf(fp, "    Size (bytes):   %-10u\n", totalsize);
00779 }
00780 
00781 
00782 //
00783 //  We need to preserve the buckets
00784 // while moving the existing data into the new locations.
00785 //
00786 // if data == NULL we are rebuilding (as opposed to check or fix)
00787 //
00788 int
00789 MultiCacheBase::rebuild(MultiCacheBase & old, int kind)
00790 {
00791   char *new_data = 0;
00792 
00793   ink_assert(store_verify(store));
00794   ink_assert(store_verify(old.store));
00795 
00796   // map in a chunk of space to use as scratch (check)
00797   // or to copy the database to.
00798   ats_scoped_fd fd(socketManager.open("/dev/zero", O_RDONLY));
00799   if (fd < 0) {
00800     Warning("unable to open /dev/zero: %d, %s", errno, strerror(errno));
00801     return -1;
00802   }
00803 
00804   new_data = (char *) mmap(0, old.totalsize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
00805 
00806   ink_assert(data != new_data);
00807   if (new_data == NULL || new_data == (caddr_t) MAP_FAILED) {
00808     Warning("unable to mmap /dev/zero for %u bytes: %d, %s", totalsize,errno, strerror(errno));
00809     return -1;
00810   }
00811   // if we are rebuilding get the original data
00812 
00813   if (!data) {
00814     ink_assert(kind == MC_REBUILD);
00815     if (old.mmap_data(true, true) < 0)
00816       return -1;
00817     memcpy(new_data, old.data, old.totalsize);
00818     old.unmap_data();
00819     // now map the new location
00820     if (mmap_data() < 0)
00821       return -1;
00822     // old.data is the copy
00823     old.data = new_data;
00824   } else {
00825     ink_assert(kind == MC_REBUILD_CHECK || kind == MC_REBUILD_FIX);
00826     if (kind == MC_REBUILD_CHECK) {
00827       // old.data is the original, data is the copy
00828       old.data = data;
00829       data = new_data;
00830     } else {
00831       memcpy(new_data, data, old.totalsize);
00832       // old.data is the copy, data is the original
00833       old.data = new_data;
00834     }
00835   }
00836 
00837   ink_assert(buckets == old.buckets);
00838 
00839   FILE *diag_output_fp = stderr;
00840 
00841   RebuildMC r;
00842 
00843   r.data = old.data;
00844 
00845   r.rebuild = kind == MC_REBUILD;
00846   r.check = kind == MC_REBUILD_CHECK;
00847   r.fix = kind == MC_REBUILD_FIX;
00848 
00849   r.deleted = 0;
00850   r.backed = 0;
00851   r.duplicates = 0;
00852   r.stale = 0;
00853   r.corrupt = 0;
00854   r.good = 0;
00855   r.total = 0;
00856 
00857   if (r.rebuild)
00858     fprintf(diag_output_fp, "New:\n");
00859   print_info(diag_output_fp);
00860   if (r.rebuild || r.fix) {
00861     fprintf(diag_output_fp, "Old:\n");
00862     old.print_info(diag_output_fp);
00863     clear_but_heap();
00864   }
00865 
00866   fprintf(diag_output_fp, "    [processing element.. ");
00867 
00868   int scan = 0;
00869   for (int l = old.levels - 1; l >= 0; l--)
00870     for (int b = 0; b < old.buckets; b++) {
00871       r.partition = partition_of_bucket(b);
00872       for (int e = 0; e < old.elements[l]; e++) {
00873         scan++;
00874         if (!(scan & 0x7FFF))
00875           fprintf(diag_output_fp, "%d ", scan);
00876         char *x = old.data + old.level_offset[l] + b * old.bucketsize[l] + e * elementsize;
00877         rebuild_element(b, x, r);
00878       }
00879     }
00880   if (scan & 0x7FFF)
00881     printf("done]\n");
00882   if (r.rebuild || r.fix)
00883     for (int p = 0; p < MULTI_CACHE_PARTITIONS; p++)
00884       sync_partition(p);
00885 
00886   fprintf(diag_output_fp, "    Usage Summary\n");
00887   fprintf(diag_output_fp, "\tTotal:      %-10d\n", r.total);
00888   if (r.good)
00889     fprintf(diag_output_fp, "\tGood:       %.2f%% (%d)\n", r.total ? ((r.good * 100.0) / r.total) : 0, r.good);
00890   if (r.deleted)
00891     fprintf(diag_output_fp, "\tDeleted:    %5.2f%% (%d)\n",
00892             r.deleted ? ((r.deleted * 100.0) / r.total) : 0.0, r.deleted);
00893   if (r.backed)
00894     fprintf(diag_output_fp, "\tBacked:     %5.2f%% (%d)\n", r.backed ? ((r.backed * 100.0) / r.total) : 0.0, r.backed);
00895   if (r.duplicates)
00896     fprintf(diag_output_fp, "\tDuplicates: %5.2f%% (%d)\n",
00897             r.duplicates ? ((r.duplicates * 100.0) / r.total) : 0.0, r.duplicates);
00898   if (r.stale)
00899     fprintf(diag_output_fp, "\tStale:      %5.2f%% (%d)\n", r.stale ? ((r.stale * 100.0) / r.total) : 0.0, r.stale);
00900   if (r.corrupt)
00901     fprintf(diag_output_fp, "\tCorrupt:    %5.2f%% (%d)\n",
00902             r.corrupt ? ((r.corrupt * 100.0) / r.total) : 0.0, r.corrupt);
00903 
00904   old.reset();
00905 
00906   return 0;
00907 }
00908 
00909 int
00910 MultiCacheBase::check(const char *config_filename, bool fix)
00911 {
00912   //  rebuild
00913   Store tStore;
00914   char t_db_filename[PATH_NAME_MAX];
00915   t_db_filename[0] = 0;
00916   int t_db_size = 0, t_db_buckets = 0;
00917   if (read_config(config_filename, tStore, t_db_filename, &t_db_size, &t_db_buckets) <= 0)
00918     return -1;
00919 
00920   MultiCacheBase *old = dup();
00921 
00922   if (old->initialize(&tStore, filename, nominal_elements, buckets) <= 0) {
00923     delete old;
00924     return -1;
00925   }
00926 
00927   int res = rebuild(*old, fix ? MC_REBUILD_FIX : MC_REBUILD_CHECK);
00928   delete old;
00929   return res;
00930 }
00931 
00932 int
00933 MultiCacheBase::sync_heap(int part)
00934 {
00935   if (heap_size) {
00936     int b_per_part = heap_size / MULTI_CACHE_PARTITIONS;
00937     if (ats_msync(data + level_offset[2] + buckets * bucketsize[2] +
00938                    b_per_part * part, b_per_part, data + totalsize, MS_SYNC) < 0)
00939       return -1;
00940   }
00941   return 0;
00942 }
00943 
00944 //
00945 // Sync a single partition
00946 //
00947 // Since we delete from the higher levels
00948 // and insert into the lower levels,
00949 // start with the higher levels to reduce the risk of duplicates.
00950 //
00951 int
00952 MultiCacheBase::sync_partition(int partition)
00953 {
00954   int res = 0;
00955   int b = first_bucket_of_partition(partition);
00956   int n = buckets_of_partition(partition);
00957   // L3
00958   if (levels > 2) {
00959     if (ats_msync(data + level_offset[2] + b * bucketsize[2], n * bucketsize[2], data + totalsize, MS_SYNC) < 0)
00960       res = -1;
00961   }
00962   // L2
00963   if (levels > 1) {
00964     if (ats_msync(data + level_offset[1] + b * bucketsize[1], n * bucketsize[1], data + totalsize, MS_SYNC) < 0)
00965       res = -1;
00966   }
00967   // L1
00968   if (ats_msync(data + b * bucketsize[0], n * bucketsize[0], data + totalsize, MS_SYNC) < 0)
00969     res = -1;
00970   return res;
00971 }
00972 
00973 int
00974 MultiCacheBase::sync_header()
00975 {
00976   *mapped_header = *(MultiCacheHeader *) this;
00977   return ats_msync((char *) mapped_header, STORE_BLOCK_SIZE, (char *) mapped_header + STORE_BLOCK_SIZE, MS_SYNC);
00978 }
00979 
00980 int
00981 MultiCacheBase::sync_all()
00982 {
00983   int res = 0, i = 0;
00984   for (i = 0; i < MULTI_CACHE_PARTITIONS; i++)
00985     if (sync_heap(i) < 0)
00986       res = -1;
00987   for (i = 0; i < MULTI_CACHE_PARTITIONS; i++)
00988     if (sync_partition(i) < 0)
00989       res = -1;
00990   if (sync_header())
00991     res = -1;
00992   return res;
00993 }
00994 
00995 //
00996 // Syncs MulitCache
00997 //
00998 struct MultiCacheSync;
00999 typedef int (MultiCacheSync::*MCacheSyncHandler) (int, void *);
01000 
01001 struct MultiCacheSync: public Continuation
01002 {
01003   int partition;
01004   MultiCacheBase *mc;
01005   Continuation *cont;
01006   int before_used;
01007 
01008   int heapEvent(int event, Event *e)
01009   {
01010     if (!partition) {
01011       before_used = mc->heap_used[mc->heap_halfspace];
01012       mc->header_snap = *(MultiCacheHeader *) mc;
01013     }
01014     if (partition < MULTI_CACHE_PARTITIONS)
01015     {
01016       mc->sync_heap(partition++);
01017       e->schedule_imm();
01018       return EVENT_CONT;
01019     }
01020     *mc->mapped_header = mc->header_snap;
01021     ink_assert(!ats_msync((char *) mc->mapped_header, STORE_BLOCK_SIZE,
01022                            (char *) mc->mapped_header + STORE_BLOCK_SIZE, MS_SYNC));
01023     partition = 0;
01024     SET_HANDLER((MCacheSyncHandler) & MultiCacheSync::mcEvent);
01025     return mcEvent(event, e);
01026   }
01027 
01028   int mcEvent(int event, Event *e)
01029   {
01030     (void) event;
01031     if (partition >= MULTI_CACHE_PARTITIONS) {
01032       cont->handleEvent(MULTI_CACHE_EVENT_SYNC, 0);
01033       Debug("multicache", "MultiCacheSync done (%d, %d)", mc->heap_used[0], mc->heap_used[1]);
01034       delete this;
01035       return EVENT_DONE;
01036     }
01037     mc->fixup_heap_offsets(partition, before_used);
01038     mc->sync_partition(partition);
01039     partition++;
01040     mutex = e->ethread->mutex;
01041     SET_HANDLER((MCacheSyncHandler) & MultiCacheSync::pauseEvent);
01042     e->schedule_in(MAX(MC_SYNC_MIN_PAUSE_TIME, HRTIME_SECONDS(hostdb_sync_frequency - 5) / MULTI_CACHE_PARTITIONS));
01043     return EVENT_CONT;
01044   }
01045 
01046   int pauseEvent(int event, Event *e)
01047   {
01048     (void) event;
01049     (void) e;
01050     if (partition < MULTI_CACHE_PARTITIONS)
01051       mutex = mc->locks[partition];
01052     else
01053       mutex = cont->mutex;
01054     SET_HANDLER((MCacheSyncHandler) & MultiCacheSync::mcEvent);
01055     e->schedule_imm();
01056     return EVENT_CONT;
01057   }
01058 
01059 MultiCacheSync(Continuation *acont, MultiCacheBase *amc):
01060   Continuation(amc->locks[0]), partition(0), mc(amc), cont(acont), before_used(0) {
01061     mutex = mc->locks[partition];
01062     SET_HANDLER((MCacheSyncHandler) & MultiCacheSync::heapEvent);
01063   }
01064 };
01065 
01066 //
01067 // Heap code
01068 //
01069 
01070 UnsunkPtrRegistry *
01071 MultiCacheBase::fixup_heap_offsets(int partition, int before_used, UnsunkPtrRegistry *r, int base)
01072 {
01073   if (!r)
01074     r = &unsunk[partition];
01075   bool found = 0;
01076   for (int i = 0; i < r->n; i++) {
01077     UnsunkPtr & p = r->ptrs[i];
01078     if (p.offset) {
01079       Debug("multicache", "fixup p.offset %d offset %d %" PRId64 " part %d",
01080             p.offset, *p.poffset, (int64_t)((char *) p.poffset - data), partition);
01081       if (*p.poffset == -(i + base) - 1) {
01082         if (halfspace_of(p.offset) != heap_halfspace) {
01083           ink_assert(0);
01084           *p.poffset = 0;
01085         } else {
01086           if (p.offset < before_used) {
01087             *p.poffset = p.offset + 1;
01088             ink_assert(*p.poffset);
01089           } else
01090             continue;
01091         }
01092       } else {
01093         Debug("multicache",
01094               "not found %" PRId64 " i %d base %d *p.poffset = %d",
01095               (int64_t)((char *) p.poffset - data), i, base, *p.poffset);
01096       }
01097       p.offset = 0;
01098       p.poffset = (int *) r->next_free;
01099       r->next_free = &p;
01100       found = true;
01101     }
01102   }
01103   if (r->next) {
01104     int s = MULTI_CACHE_UNSUNK_PTR_BLOCK_SIZE(totalelements);
01105     r->next = fixup_heap_offsets(partition, before_used, r->next, base + s);
01106   }
01107   if (!r->next && !found && r != &unsunk[partition]) {
01108     delete r;
01109     return NULL;
01110   }
01111   return r;
01112 }
01113 
01114 struct OffsetTable
01115 {
01116   int new_offset;
01117   int *poffset;
01118 };
01119 
01120 struct MultiCacheHeapGC;
01121 typedef int (MultiCacheHeapGC::*MCacheHeapGCHandler) (int, void *);
01122 struct MultiCacheHeapGC: public Continuation
01123 {
01124   Continuation *cont;
01125   MultiCacheBase *mc;
01126   int partition;
01127   int n_offsets;
01128   OffsetTable *offset_table;
01129 
01130   int startEvent(int event, Event *e)
01131   {
01132     (void) event;
01133     if (partition < MULTI_CACHE_PARTITIONS)
01134     {
01135 
01136       // copy heap data
01137 
01138       char *before = mc->heap + mc->heap_used[mc->heap_halfspace];
01139         mc->copy_heap(partition, this);
01140       char *after = mc->heap + mc->heap_used[mc->heap_halfspace];
01141 
01142       // sync new heap data and header (used)
01143 
01144       if (after - before > 0)
01145       {
01146         ink_assert(!ats_msync(before, after - before, mc->heap + mc->totalsize, MS_SYNC));
01147         ink_assert(!ats_msync((char *) mc->mapped_header, STORE_BLOCK_SIZE,
01148                                (char *) mc->mapped_header + STORE_BLOCK_SIZE, MS_SYNC));
01149       }
01150       // update table to point to new entries
01151 
01152       for (int i = 0; i < n_offsets; i++) {
01153         int *i1, i2;
01154         // BAD CODE GENERATION ON THE ALPHA
01155         //*(offset_table[i].poffset) = offset_table[i].new_offset + 1;
01156         i1 = offset_table[i].poffset;
01157         i2 = offset_table[i].new_offset + 1;
01158         *i1 = i2;
01159       }
01160       n_offsets = 0;
01161       mc->sync_partition(partition);
01162       partition++;
01163       if (partition < MULTI_CACHE_PARTITIONS)
01164         mutex = mc->locks[partition];
01165       else
01166         mutex = cont->mutex;
01167       e->schedule_in(MAX(MC_SYNC_MIN_PAUSE_TIME, HRTIME_SECONDS(hostdb_sync_frequency - 5) / MULTI_CACHE_PARTITIONS));
01168       return EVENT_CONT;
01169     }
01170     mc->heap_used[mc->heap_halfspace ? 0 : 1] = 8;      // skip 0
01171     cont->handleEvent(MULTI_CACHE_EVENT_SYNC, 0);
01172     Debug("multicache", "MultiCacheHeapGC done");
01173     delete this;
01174     return EVENT_DONE;
01175   }
01176 
01177 MultiCacheHeapGC(Continuation *acont, MultiCacheBase *amc):
01178   Continuation(amc->locks[0]), cont(acont), mc(amc), partition(0), n_offsets(0) {
01179 
01180     SET_HANDLER((MCacheHeapGCHandler) & MultiCacheHeapGC::startEvent);
01181     offset_table = (OffsetTable *)ats_malloc(sizeof(OffsetTable) *
01182         ((mc->totalelements / MULTI_CACHE_PARTITIONS) + mc->elements[mc->levels - 1] * 3 + 1));
01183     // flip halfspaces
01184     mutex = mc->locks[partition];
01185     mc->heap_halfspace = mc->heap_halfspace ? 0 : 1;
01186   }
01187   ~MultiCacheHeapGC() {
01188     ats_free(offset_table);
01189   }
01190 };
01191 
01192 void
01193 MultiCacheBase::sync_partitions(Continuation *cont)
01194 {
01195   // don't try to sync if we were not correctly initialized
01196   if (data && mapped_header) {
01197     if (heap_used[heap_halfspace] > halfspace_size() * MULTI_CACHE_HEAP_HIGH_WATER)
01198       eventProcessor.schedule_imm(new MultiCacheHeapGC(cont, this), ET_CALL);
01199     else
01200       eventProcessor.schedule_imm(new MultiCacheSync(cont, this), ET_CALL);
01201   }
01202 }
01203 
01204 void
01205 MultiCacheBase::copy_heap_data(char *src, int s, int *pi, int partition, MultiCacheHeapGC *gc)
01206 {
01207   char *dest = (char *) alloc(NULL, s);
01208   Debug("multicache", "copy %p to %p", src, dest);
01209   if (dest) {
01210     memcpy(dest, src, s);
01211     if (*pi < 0) {              // already in the unsunk ptr registry, ok to change there
01212       UnsunkPtr *ptr = unsunk[partition].ptr(-*pi - 1);
01213       if (ptr->poffset == pi)
01214         ptr->offset = dest - heap;
01215       else {
01216         ink_assert(0);
01217         *pi = 0;
01218       }
01219     } else {
01220       gc->offset_table[gc->n_offsets].new_offset = dest - heap;
01221       gc->offset_table[gc->n_offsets].poffset = pi;
01222       gc->n_offsets++;
01223     }
01224   } else {
01225     ink_assert(0);
01226     *pi = 0;
01227   }
01228 }
01229 
01230 UnsunkPtrRegistry::UnsunkPtrRegistry()
01231 :mc(NULL), n(0), ptrs(NULL), next_free(NULL), next(NULL)
01232 {
01233 }
01234 
01235 UnsunkPtrRegistry::~UnsunkPtrRegistry()
01236 {
01237   ats_free(ptrs);
01238 }
01239 
01240 void
01241 UnsunkPtrRegistry::alloc_data()
01242 {
01243   int bs = MULTI_CACHE_UNSUNK_PTR_BLOCK_SIZE(mc->totalelements);
01244   size_t s = bs * sizeof(UnsunkPtr);
01245   ptrs = (UnsunkPtr *)ats_malloc(s);
01246   for (int i = 0; i < bs; i++) {
01247     ptrs[i].offset = 0;
01248     ptrs[i].poffset = (int *) &ptrs[i + 1];
01249   }
01250   ptrs[bs - 1].poffset = NULL;
01251   next_free = ptrs;
01252   n = bs;
01253 }
01254 
01255 UnsunkPtr *
01256 UnsunkPtrRegistry::alloc(int *poffset, int base)
01257 {
01258   if (next_free) {
01259     UnsunkPtr *res = next_free;
01260     next_free = (UnsunkPtr *) next_free->poffset;
01261     *poffset = -(base + (res - ptrs)) - 1;
01262     ink_assert(*poffset);
01263     return res;
01264   } else {
01265     if (!ptrs) {
01266       alloc_data();
01267       return alloc(poffset, base);
01268     }
01269     if (!next) {
01270       next = new UnsunkPtrRegistry;
01271       next->mc = mc;
01272     }
01273     int s = MULTI_CACHE_UNSUNK_PTR_BLOCK_SIZE(mc->totalelements);
01274     return next->alloc(poffset, base + s);
01275   }
01276 }
01277 
01278 void *
01279 MultiCacheBase::alloc(int *poffset, int asize)
01280 {
01281   int h = heap_halfspace;
01282   int size = (asize + MULTI_CACHE_HEAP_ALIGNMENT - 1) & ~(MULTI_CACHE_HEAP_ALIGNMENT - 1);
01283   int o = ink_atomic_increment((int *) &heap_used[h], size);
01284 
01285   if (o + size > halfspace_size()) {
01286     ink_atomic_increment((int *) &heap_used[h], -size);
01287     ink_assert(!"out of space");
01288     if (poffset)
01289       *poffset = 0;
01290     return NULL;
01291   }
01292   int offset = (h ? halfspace_size() : 0) + o;
01293   char *p = heap + offset;
01294   if (poffset) {
01295     int part = ptr_to_partition((char *) poffset);
01296     if (part < 0)
01297       return NULL;
01298     UnsunkPtr *up = unsunk[part].alloc(poffset);
01299     up->offset = offset;
01300     up->poffset = poffset;
01301     Debug("multicache", "alloc unsunk %d at %" PRId64 " part %d offset %d",
01302           *poffset, (int64_t)((char *) poffset - data), part, offset);
01303   }
01304   return (void *) p;
01305 }
01306 
01307 UnsunkPtr *
01308 UnsunkPtrRegistry::ptr(int i)
01309 {
01310   if (i >= n) {
01311     if (!next)
01312       return NULL;
01313     else
01314       return next->ptr(i - n);
01315   } else {
01316     if (!ptrs)
01317       return NULL;
01318     return &ptrs[i];
01319   }
01320 }
01321 
01322 void *
01323 MultiCacheBase::ptr(int *poffset, int partition)
01324 {
01325   int o = *poffset;
01326   Debug("multicache", "ptr %" PRId64 " part %d %d", (int64_t)((char *) poffset - data), partition, o);
01327   if (o > 0) {
01328     if (!valid_offset(o)) {
01329       ink_assert(!"bad offset");
01330       *poffset = 0;
01331       return NULL;
01332     }
01333     return (void *) (heap + o - 1);
01334   }
01335   if (!o)
01336     return NULL;
01337   UnsunkPtr *p = unsunk[partition].ptr(-o - 1);
01338   if (!p)
01339     return NULL;
01340   if (p->poffset != poffset)
01341     return NULL;
01342   return (void *) (heap + p->offset);
01343 }
01344 
01345 void
01346 MultiCacheBase::update(int *poffset, int *old_poffset)
01347 {
01348   int o = *poffset;
01349   Debug("multicache", "updating %" PRId64 " %d", (int64_t)((char *) poffset - data), o);
01350   if (o > 0) {
01351     if (!valid_offset(o)) {
01352       ink_assert(!"bad poffset");
01353       *poffset = 0;
01354     }
01355     return;
01356   }
01357   if (!o)
01358     return;
01359 
01360   int part = ptr_to_partition((char *) poffset);
01361 
01362   if (part < 0)
01363     return;
01364 
01365   UnsunkPtr *p = unsunk[part].ptr(-*old_poffset - 1);
01366   if (!p || p->poffset != old_poffset) {
01367     *poffset = 0;
01368     return;
01369   }
01370   ink_assert(p->poffset != poffset);
01371   UnsunkPtr *n = unsunk[part].alloc(poffset);
01372   n->poffset = poffset;
01373   n->offset = p->offset;
01374 }
01375 
01376 int
01377 MultiCacheBase::ptr_to_partition(char *ptr)
01378 {
01379   int o = ptr - data;
01380   if (o < level_offset[0])
01381     return -1;
01382   if (o < level_offset[1])
01383     return partition_of_bucket((o - level_offset[0]) / bucketsize[0]);
01384   if (o < level_offset[2])
01385     return partition_of_bucket((o - level_offset[1]) / bucketsize[1]);
01386   if (o < level_offset[2] + elements[2] * elementsize)
01387     return partition_of_bucket((o - level_offset[2]) / bucketsize[2]);
01388   return -1;
01389 }
01390 
01391 
01392 void
01393 stealStore(Store & s, int blocks)
01394 {
01395   if (s.read_config())
01396     return;
01397   Store tStore;
01398   MultiCacheBase dummy;
01399   if (dummy.read_config("hostdb.config", tStore) > 0) {
01400     Store dStore;
01401     s.try_realloc(tStore, dStore);
01402   }
01403   tStore.delete_all();
01404   if (dummy.read_config("dir.config", tStore) > 0) {
01405     Store dStore;
01406     s.try_realloc(tStore, dStore);
01407   }
01408   tStore.delete_all();
01409   if (dummy.read_config("alt.config", tStore) > 0) {
01410     Store dStore;
01411     s.try_realloc(tStore, dStore);
01412   }
01413   // grab some end portion of some block... so as not to damage the
01414   // pool header
01415   for (unsigned d = 0; d < s.n_disks; ) {
01416     Span *ds = s.disk[d];
01417     while (ds) {
01418       if (!blocks)
01419         ds->blocks = 0;
01420       else {
01421         int b = blocks;
01422         if ((int) ds->blocks < blocks)
01423           b = ds->blocks;
01424         if (ds->file_pathname)
01425           ds->offset += (ds->blocks - b);
01426         ds->blocks = b;
01427         blocks -= b;
01428       }
01429       ds = ds->link.next;
01430     }
01431     d++;
01432   }
01433 }

Generated by  doxygen 1.7.1