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 #include "libts.h"
00032 #include "I_Layout.h"
00033 #include "P_HostDB.h"
00034 #include "P_MultiCache.h"
00035 #include "P_EventSystem.h"
00036 #include "ink_file.h"
00037
00038 static const int MC_SYNC_MIN_PAUSE_TIME = HRTIME_MSECONDS(200);
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
00101
00102
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
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
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
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;
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
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;
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;
00303
00304
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
00353
00354 {
00355
00356 Store tStore;
00357 store->dup(tStore);
00358 Store *saved = store;
00359 store = &tStore;
00360
00361 char *cur = 0;
00362
00363
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
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
00399
00400
00401
00402
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
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
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
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
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
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
00594 ink_assert(!db_filename || !strcmp(t_db_filename, db_filename));
00595 if (!db_filename)
00596 db_filename = t_db_filename;
00597
00598
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
00609 Store diff;
00610
00611 s->try_realloc(cStore, diff);
00612 if (diff.n_disks && !reconfigure)
00613 goto LfailConfig;
00614
00615
00616 if (diff.n_disks || change) {
00617
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
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 {
00777 fprintf(fp, " Elements: %-10d\n", totalelements);
00778 fprintf(fp, " Size (bytes): %-10u\n", totalsize);
00779 }
00780
00781
00782
00783
00784
00785
00786
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
00797
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
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
00820 if (mmap_data() < 0)
00821 return -1;
00822
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
00828 old.data = data;
00829 data = new_data;
00830 } else {
00831 memcpy(new_data, data, old.totalsize);
00832
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
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
00946
00947
00948
00949
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
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
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
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
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
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
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
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
01151
01152 for (int i = 0; i < n_offsets; i++) {
01153 int *i1, i2;
01154
01155
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;
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
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
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) {
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
01414
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 }