]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store/Disks.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / store / Disks.cc
CommitLineData
2745fea5 1/*
f70aedc4 2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
2745fea5
AR
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9/* DEBUG: section 47 Store Directory Routines */
10
11#include "squid.h"
5d84beb5
EB
12#include "cache_cf.h"
13#include "ConfigParser.h"
2745fea5
AR
14#include "Debug.h"
15#include "globals.h"
16#include "profiler/Profiler.h"
5d84beb5 17#include "sbuf/Stream.h"
2745fea5
AR
18#include "SquidConfig.h"
19#include "Store.h"
20#include "store/Disk.h"
21#include "store/Disks.h"
8ecbe78d 22#include "store_rebuild.h"
70ac5b29 23#include "StoreFileSystem.h"
2745fea5 24#include "swap_log_op.h"
5d84beb5 25#include "tools.h"
2745fea5
AR
26#include "util.h" // for tvSubDsec() which should be in SquidTime.h
27
5d84beb5
EB
28typedef SwapDir *STDIRSELECT(const StoreEntry *e);
29
2745fea5
AR
30static STDIRSELECT storeDirSelectSwapDirRoundRobin;
31static STDIRSELECT storeDirSelectSwapDirLeastLoad;
024aeeee 32/**
2745fea5
AR
33 * This function pointer is set according to 'store_dir_select_algorithm'
34 * in squid.conf.
35 */
5d84beb5 36static STDIRSELECT *storeDirSelectSwapDir = storeDirSelectSwapDirLeastLoad;
2745fea5 37
5ca027f0
AR
38/// The entry size to use for Disk::canStore() size limit checks.
39/// This is an optimization to avoid similar calculations in every cache_dir.
40static int64_t
41objectSizeForDirSelection(const StoreEntry &entry)
42{
43 // entry.objectLen() is negative here when we are still STORE_PENDING
44 int64_t minSize = entry.mem_obj->expectedReplySize();
45
46 // If entry size is unknown, use already accumulated bytes as an estimate.
47 // Controller::accumulateMore() guarantees that there are enough of them.
48 if (minSize < 0)
49 minSize = entry.mem_obj->endOffset();
50
51 assert(minSize >= 0);
52 minSize += entry.mem_obj->swap_hdr_sz;
53 return minSize;
54}
55
5d84beb5
EB
56/// TODO: Remove when cache_dir-iterating functions are converted to Disks methods
57static SwapDir &
58SwapDirByIndex(const int i)
59{
60 assert(i >= 0);
61 assert(i < Config.cacheSwap.n_allocated);
62 const auto sd = INDEXSD(i);
63 assert(sd);
64 return *sd;
65}
66
024aeeee 67/**
2745fea5
AR
68 * This new selection scheme simply does round-robin on all SwapDirs.
69 * A SwapDir is skipped if it is over the max_size (100%) limit, or
70 * overloaded.
71 */
5d84beb5 72static SwapDir *
2745fea5
AR
73storeDirSelectSwapDirRoundRobin(const StoreEntry * e)
74{
5ca027f0 75 const int64_t objsize = objectSizeForDirSelection(*e);
2745fea5
AR
76
77 // Increment the first candidate once per selection (not once per
78 // iteration) to reduce bias when some disk(s) attract more entries.
79 static int firstCandidate = 0;
80 if (++firstCandidate >= Config.cacheSwap.n_configured)
81 firstCandidate = 0;
82
83 for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
84 const int dirn = (firstCandidate + i) % Config.cacheSwap.n_configured;
5d84beb5 85 auto &dir = SwapDirByIndex(dirn);
2745fea5
AR
86
87 int load = 0;
5d84beb5 88 if (!dir.canStore(*e, objsize, load))
2745fea5
AR
89 continue;
90
91 if (load < 0 || load > 1000) {
92 continue;
93 }
94
5d84beb5 95 return &dir;
2745fea5
AR
96 }
97
5d84beb5 98 return nullptr;
2745fea5
AR
99}
100
024aeeee 101/**
2745fea5
AR
102 * Spread load across all of the store directories
103 *
104 * Note: We should modify this later on to prefer sticking objects
105 * in the *tightest fit* swapdir to conserve space, along with the
106 * actual swapdir usage. But for now, this hack will do while
107 * testing, so you should order your swapdirs in the config file
108 * from smallest max-size= to largest max-size=.
109 *
110 * We also have to choose nleast == nconf since we need to consider
111 * ALL swapdirs, regardless of state. Again, this is a hack while
112 * we sort out the real usefulness of this algorithm.
113 */
5d84beb5 114static SwapDir *
2745fea5
AR
115storeDirSelectSwapDirLeastLoad(const StoreEntry * e)
116{
117 int64_t most_free = 0;
5ca027f0 118 int64_t best_objsize = -1;
2745fea5
AR
119 int least_load = INT_MAX;
120 int load;
5d84beb5 121 SwapDir *selectedDir = nullptr;
2745fea5 122 int i;
2745fea5 123
5ca027f0 124 const int64_t objsize = objectSizeForDirSelection(*e);
2745fea5
AR
125
126 for (i = 0; i < Config.cacheSwap.n_configured; ++i) {
5d84beb5
EB
127 auto &sd = SwapDirByIndex(i);
128 sd.flags.selected = false;
2745fea5 129
5d84beb5 130 if (!sd.canStore(*e, objsize, load))
2745fea5
AR
131 continue;
132
133 if (load < 0 || load > 1000)
134 continue;
135
136 if (load > least_load)
137 continue;
138
5d84beb5 139 const int64_t cur_free = sd.maxSize() - sd.currentSize();
2745fea5
AR
140
141 /* If the load is equal, then look in more details */
142 if (load == least_load) {
5ca027f0
AR
143 /* best max-size fit */
144 if (best_objsize != -1) {
145 // cache_dir with the smallest max-size gets the known-size object
146 // cache_dir with the largest max-size gets the unknown-size object
5d84beb5
EB
147 if ((objsize != -1 && sd.maxObjectSize() > best_objsize) ||
148 (objsize == -1 && sd.maxObjectSize() < best_objsize))
2745fea5 149 continue;
5ca027f0 150 }
2745fea5
AR
151
152 /* most free */
153 if (cur_free < most_free)
154 continue;
155 }
156
157 least_load = load;
5d84beb5 158 best_objsize = sd.maxObjectSize();
2745fea5 159 most_free = cur_free;
5d84beb5 160 selectedDir = &sd;
2745fea5
AR
161 }
162
5d84beb5
EB
163 if (selectedDir)
164 selectedDir->flags.selected = true;
2745fea5 165
5d84beb5 166 return selectedDir;
2745fea5
AR
167}
168
5ca027f0
AR
169Store::Disks::Disks():
170 largestMinimumObjectSize(-1),
171 largestMaximumObjectSize(-1),
172 secondLargestMaximumObjectSize(-1)
173{
174}
175
2745fea5
AR
176SwapDir *
177Store::Disks::store(int const x) const
178{
5d84beb5 179 return &SwapDirByIndex(x);
2745fea5
AR
180}
181
182SwapDir &
daed75a9 183Store::Disks::Dir(const int i)
2745fea5 184{
5d84beb5 185 return SwapDirByIndex(i);
2745fea5
AR
186}
187
188int
189Store::Disks::callback()
190{
191 int result = 0;
192 int j;
193 static int ndir = 0;
194
195 do {
196 j = 0;
197
198 for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
199 if (ndir >= Config.cacheSwap.n_configured)
200 ndir = ndir % Config.cacheSwap.n_configured;
201
202 int temp_result = store(ndir)->callback();
203
204 ++ndir;
205
206 j += temp_result;
207
208 result += temp_result;
209
210 if (j > 100)
211 fatal ("too much io\n");
212 }
213 } while (j > 0);
214
215 ++ndir;
216
217 return result;
218}
219
220void
221Store::Disks::create()
222{
223 if (Config.cacheSwap.n_configured == 0) {
224 debugs(0, DBG_PARSE_NOTE(DBG_CRITICAL), "No cache_dir stores are configured.");
225 }
226
227 for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
daed75a9 228 if (Dir(i).active())
2745fea5
AR
229 store(i)->create();
230 }
231}
232
233StoreEntry *
234Store::Disks::get(const cache_key *key)
235{
236 if (const int cacheDirs = Config.cacheSwap.n_configured) {
237 // ask each cache_dir until the entry is found; use static starting
238 // point to avoid asking the same subset of disks more often
239 // TODO: coordinate with put() to be able to guess the right disk often
240 static int idx = 0;
241 for (int n = 0; n < cacheDirs; ++n) {
242 idx = (idx + 1) % cacheDirs;
5d84beb5
EB
243 auto &sd = Dir(idx);
244 if (!sd.active())
2745fea5
AR
245 continue;
246
5d84beb5 247 if (auto e = sd.get(key)) {
2745fea5
AR
248 debugs(20, 7, "cache_dir " << idx << " has: " << *e);
249 return e;
250 }
251 }
252 }
253
254 debugs(20, 6, "none of " << Config.cacheSwap.n_configured <<
255 " cache_dirs have " << storeKeyText(key));
256 return nullptr;
257}
258
259void
260Store::Disks::init()
261{
262 if (Config.Store.objectsPerBucket <= 0)
263 fatal("'store_objects_per_bucket' should be larger than 0.");
264
265 if (Config.Store.avgObjectSize <= 0)
266 fatal("'store_avg_object_size' should be larger than 0.");
267
268 /* Calculate size of hash table (maximum currently 64k buckets). */
269 /* this is very bogus, its specific to the any Store maintaining an
270 * in-core index, not global */
271 size_t buckets = (Store::Root().maxSize() + Config.memMaxSize) / Config.Store.avgObjectSize;
272 debugs(20, DBG_IMPORTANT, "Swap maxSize " << (Store::Root().maxSize() >> 10) <<
273 " + " << ( Config.memMaxSize >> 10) << " KB, estimated " << buckets << " objects");
274 buckets /= Config.Store.objectsPerBucket;
275 debugs(20, DBG_IMPORTANT, "Target number of buckets: " << buckets);
276 /* ideally the full scan period should be configurable, for the
277 * moment it remains at approximately 24 hours. */
278 store_hash_buckets = storeKeyHashBuckets(buckets);
279 debugs(20, DBG_IMPORTANT, "Using " << store_hash_buckets << " Store buckets");
280 debugs(20, DBG_IMPORTANT, "Max Mem size: " << ( Config.memMaxSize >> 10) << " KB" <<
281 (Config.memShared ? " [shared]" : ""));
282 debugs(20, DBG_IMPORTANT, "Max Swap size: " << (Store::Root().maxSize() >> 10) << " KB");
283
284 store_table = hash_create(storeKeyHashCmp,
285 store_hash_buckets, storeKeyHashHash);
286
8ecbe78d
EB
287 // Increment _before_ any possible storeRebuildComplete() calls so that
288 // storeRebuildComplete() can reliably detect when all disks are done. The
289 // level is decremented in each corresponding storeRebuildComplete() call.
290 StoreController::store_dirs_rebuilding += Config.cacheSwap.n_configured;
291
2745fea5
AR
292 for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
293 /* this starts a search of the store dirs, loading their
294 * index. under the new Store api this should be
295 * driven by the StoreHashIndex, not by each store.
296 *
297 * That is, the HashIndex should perform a search of each dir it is
298 * indexing to do the hash insertions. The search is then able to
299 * decide 'from-memory', or 'from-clean-log' or 'from-dirty-log' or
300 * 'from-no-log'.
301 *
302 * Step 1: make the store rebuilds use a search internally
303 * Step 2: change the search logic to use the four modes described
304 * above
305 * Step 3: have the hash index walk the searches itself.
306 */
daed75a9 307 if (Dir(i).active())
2745fea5 308 store(i)->init();
8ecbe78d
EB
309 else
310 storeRebuildComplete(nullptr);
2745fea5
AR
311 }
312
313 if (strcasecmp(Config.store_dir_select_algorithm, "round-robin") == 0) {
314 storeDirSelectSwapDir = storeDirSelectSwapDirRoundRobin;
315 debugs(47, DBG_IMPORTANT, "Using Round Robin store dir selection");
316 } else {
317 storeDirSelectSwapDir = storeDirSelectSwapDirLeastLoad;
318 debugs(47, DBG_IMPORTANT, "Using Least Load store dir selection");
319 }
320}
321
322uint64_t
323Store::Disks::maxSize() const
324{
325 uint64_t result = 0;
326
327 for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
daed75a9 328 if (Dir(i).doReportStat())
2745fea5
AR
329 result += store(i)->maxSize();
330 }
331
332 return result;
333}
334
335uint64_t
336Store::Disks::minSize() const
337{
338 uint64_t result = 0;
339
340 for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
daed75a9 341 if (Dir(i).doReportStat())
2745fea5
AR
342 result += store(i)->minSize();
343 }
344
345 return result;
346}
347
348uint64_t
349Store::Disks::currentSize() const
350{
351 uint64_t result = 0;
352
353 for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
daed75a9 354 if (Dir(i).doReportStat())
2745fea5
AR
355 result += store(i)->currentSize();
356 }
357
358 return result;
359}
360
361uint64_t
362Store::Disks::currentCount() const
363{
364 uint64_t result = 0;
365
366 for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
daed75a9 367 if (Dir(i).doReportStat())
2745fea5
AR
368 result += store(i)->currentCount();
369 }
370
371 return result;
372}
373
374int64_t
375Store::Disks::maxObjectSize() const
376{
5ca027f0
AR
377 return largestMaximumObjectSize;
378}
379
380void
5d84beb5 381Store::Disks::configure()
5ca027f0 382{
5d84beb5
EB
383 if (!Config.cacheSwap.swapDirs)
384 Controller::store_dirs_rebuilding = 0; // nothing to index
385
5ca027f0
AR
386 largestMinimumObjectSize = -1;
387 largestMaximumObjectSize = -1;
388 secondLargestMaximumObjectSize = -1;
2745fea5 389
5d84beb5
EB
390 Config.cacheSwap.n_strands = 0;
391
2745fea5 392 for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
5d84beb5
EB
393 auto &disk = Dir(i);
394 if (disk.needsDiskStrand()) {
395 assert(InDaemonMode());
396 // XXX: Do not pretend to support disk.disker changes during reconfiguration
397 disk.disker = Config.workers + (++Config.cacheSwap.n_strands);
398 }
399
5ca027f0
AR
400 if (!disk.active())
401 continue;
402
403 if (disk.minObjectSize() > largestMinimumObjectSize)
404 largestMinimumObjectSize = disk.minObjectSize();
405
406 const auto diskMaxObjectSize = disk.maxObjectSize();
407 if (diskMaxObjectSize > largestMaximumObjectSize) {
408 if (largestMaximumObjectSize >= 0) // was set
409 secondLargestMaximumObjectSize = largestMaximumObjectSize;
410 largestMaximumObjectSize = diskMaxObjectSize;
411 }
2745fea5 412 }
5ca027f0 413}
2745fea5 414
5d84beb5
EB
415void
416Store::Disks::Parse(DiskConfig &swap)
417{
418 const auto typeStr = ConfigParser::NextToken();
419 if (!typeStr)
420 throw TextException("missing cache_dir parameter: storage type", Here());
421
422 const auto pathStr = ConfigParser::NextToken();
423 if (!pathStr)
424 throw TextException("missing cache_dir parameter: directory name", Here());
425
426 const auto fs = StoreFileSystem::FindByType(typeStr);
427 if (!fs) {
428 debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: This proxy does not support the '" << typeStr << "' cache type. Ignoring.");
429 return;
430 }
431
432 const auto fsType = fs->type();
433
434 // check for the existing cache_dir
435 // XXX: This code mistreats duplicated cache_dir entries (that should be fatal).
436 for (int i = 0; i < swap.n_configured; ++i) {
437 auto &disk = Dir(i);
438 if ((strcasecmp(pathStr, disk.path)) == 0) {
439 /* this is specific to on-fs Stores. The right
440 * way to handle this is probably to have a mapping
441 * from paths to stores, and have on-fs stores
442 * register with that, and lookip in that in their
443 * own setup logic. RBC 20041225. TODO.
444 */
445
446 if (strcmp(disk.type(), fsType) == 0)
447 disk.reconfigure();
448 else
449 debugs(3, DBG_CRITICAL, "ERROR: Can't change type of existing cache_dir " <<
450 disk.type() << " " << disk.path << " to " << fsType << ". Restart required");
451
452 return;
453 }
454 }
455
456 const int cacheDirCountLimit = 64; // StoreEntry::swap_dirn is a signed 7-bit integer
457 if (swap.n_configured >= cacheDirCountLimit)
458 throw TextException(ToSBuf("Squid cannot handle more than ", cacheDirCountLimit, " cache_dir directives"), Here());
459
460 // create a new cache_dir
461 allocate_new_swapdir(swap);
462 swap.swapDirs[swap.n_configured] = fs->createSwapDir();
463 auto &disk = Dir(swap.n_configured);
464 disk.parse(swap.n_configured, pathStr);
465 ++swap.n_configured;
466}
467
468void
469Store::Disks::Dump(const DiskConfig &swap, StoreEntry &entry, const char *name)
470{
70ac5b29 471 for (int i = 0; i < swap.n_configured; ++i) {
472 const auto &disk = Dir(i);
473 storeAppendPrintf(&entry, "%s %s %s", name, disk.type(), disk.path);
474 disk.dump(entry);
475 storeAppendPrintf(&entry, "\n");
476 }
5d84beb5
EB
477}
478
5ca027f0
AR
479int64_t
480Store::Disks::accumulateMore(const StoreEntry &entry) const
481{
482 const auto accumulated = entry.mem_obj->availableForSwapOut();
483
82bee387 484 /*
5ca027f0
AR
485 * Keep accumulating more bytes until the set of disks eligible to accept
486 * the entry becomes stable, and, hence, accumulating more is not going to
487 * affect the cache_dir selection. A stable set is usually reached
488 * immediately (or soon) because most configurations either do not use
489 * cache_dirs with explicit min-size/max-size limits or use the same
490 * max-size limit for all cache_dirs (and low min-size limits).
491 */
492
493 // Can the set of min-size cache_dirs accepting this entry change?
494 if (accumulated < largestMinimumObjectSize)
495 return largestMinimumObjectSize - accumulated;
496
497 // Can the set of max-size cache_dirs accepting this entry change
498 // (other than when the entry exceeds the largest maximum; see below)?
499 if (accumulated <= secondLargestMaximumObjectSize)
500 return secondLargestMaximumObjectSize - accumulated + 1;
501
82bee387 502 /*
5ca027f0
AR
503 * Checking largestMaximumObjectSize instead eliminates the risk of starting
504 * to swap out an entry that later grows too big, but also implies huge
505 * accumulation in most environments. Accumulating huge entries not only
506 * consumes lots of RAM but also creates a burst of doPages() write requests
507 * that overwhelm the disk. To avoid these problems, we take the risk and
508 * allow swap out now. The disk will quit swapping out if the entry
509 * eventually grows too big for its selected cache_dir.
510 */
511 debugs(20, 3, "no: " << accumulated << '>' <<
512 secondLargestMaximumObjectSize << ',' << largestMinimumObjectSize);
513 return 0;
2745fea5
AR
514}
515
516void
517Store::Disks::getStats(StoreInfoStats &stats) const
518{
519 // accumulate per-disk cache stats
520 for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
521 StoreInfoStats dirStats;
522 store(i)->getStats(dirStats);
523 stats += dirStats;
524 }
525
526 // common to all disks
527 stats.swap.open_disk_fd = store_open_disk_fd;
528
529 // memory cache stats are collected in StoreController::getStats(), for now
530}
531
532void
533Store::Disks::stat(StoreEntry & output) const
534{
535 int i;
536
537 /* Now go through each store, calling its stat routine */
538
539 for (i = 0; i < Config.cacheSwap.n_configured; ++i) {
540 storeAppendPrintf(&output, "\n");
541 store(i)->stat(output);
542 }
543}
544
545void
546Store::Disks::reference(StoreEntry &e)
547{
548 e.disk().reference(e);
549}
550
551bool
552Store::Disks::dereference(StoreEntry &e)
553{
554 return e.disk().dereference(e);
555}
556
abf396ec
AR
557void
558Store::Disks::updateHeaders(StoreEntry *e)
559{
560 Must(e);
561 return e->disk().updateHeaders(e);
562}
563
2745fea5
AR
564void
565Store::Disks::maintain()
566{
567 int i;
568 /* walk each fs */
569
570 for (i = 0; i < Config.cacheSwap.n_configured; ++i) {
2f8abb64 571 /* XXX FixMe: This should be done "in parallel" on the different
2745fea5
AR
572 * cache_dirs, not one at a time.
573 */
574 /* call the maintain function .. */
575 store(i)->maintain();
576 }
577}
578
579void
580Store::Disks::sync()
581{
582 for (int i = 0; i < Config.cacheSwap.n_configured; ++i)
583 store(i)->sync();
584}
585
7d84d4ca 586void
4310f8b0
EB
587Store::Disks::evictCached(StoreEntry &e) {
588 if (e.hasDisk()) {
589 // TODO: move into Fs::Ufs::UFSSwapDir::evictCached()
590 if (!EBIT_TEST(e.flags, KEY_PRIVATE)) {
591 // log before evictCached() below may clear hasDisk()
592 storeDirSwapLog(&e, SWAP_LOG_DEL);
593 }
594
595 e.disk().evictCached(e);
596 return;
597 }
598
599 if (const auto key = e.publicKey())
600 evictIfFound(key);
2745fea5
AR
601}
602
7d84d4ca 603void
4310f8b0
EB
604Store::Disks::evictIfFound(const cache_key *key)
605{
606 for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
daed75a9
EB
607 if (Dir(i).active())
608 Dir(i).evictIfFound(key);
4310f8b0 609 }
2745fea5
AR
610}
611
612bool
4310f8b0 613Store::Disks::anchorToCache(StoreEntry &entry, bool &inSync)
2745fea5
AR
614{
615 if (const int cacheDirs = Config.cacheSwap.n_configured) {
616 // ask each cache_dir until the entry is found; use static starting
617 // point to avoid asking the same subset of disks more often
618 // TODO: coordinate with put() to be able to guess the right disk often
619 static int idx = 0;
620 for (int n = 0; n < cacheDirs; ++n) {
621 idx = (idx + 1) % cacheDirs;
daed75a9 622 SwapDir &sd = Dir(idx);
2745fea5
AR
623 if (!sd.active())
624 continue;
625
4310f8b0
EB
626 if (sd.anchorToCache(entry, inSync)) {
627 debugs(20, 3, "cache_dir " << idx << " anchors " << entry);
2745fea5
AR
628 return true;
629 }
630 }
631 }
632
633 debugs(20, 4, "none of " << Config.cacheSwap.n_configured <<
4310f8b0 634 " cache_dirs have " << entry);
2745fea5
AR
635 return false;
636}
637
638bool
4310f8b0 639Store::Disks::updateAnchored(StoreEntry &entry)
2745fea5 640{
4310f8b0 641 return entry.hasDisk() &&
5d84beb5 642 entry.disk().updateAnchored(entry);
2745fea5
AR
643}
644
1a210de4 645bool
daed75a9 646Store::Disks::SmpAware()
1a210de4
EB
647{
648 for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
649 // A mix is not supported, but we conservatively check every
650 // dir because features like collapsed revalidation should
651 // currently be disabled if any dir is SMP-aware
daed75a9 652 if (Dir(i).smpAware())
1a210de4
EB
653 return true;
654 }
655 return false;
656}
657
5d84beb5
EB
658SwapDir *
659Store::Disks::SelectSwapDir(const StoreEntry *e)
660{
661 return storeDirSelectSwapDir(e);
662}
663
4310f8b0
EB
664bool
665Store::Disks::hasReadableEntry(const StoreEntry &e) const
666{
667 for (int i = 0; i < Config.cacheSwap.n_configured; ++i)
daed75a9 668 if (Dir(i).active() && Dir(i).hasReadableEntry(e))
4310f8b0
EB
669 return true;
670 return false;
671}
2745fea5
AR
672
673void
674storeDirOpenSwapLogs()
675{
676 for (int dirn = 0; dirn < Config.cacheSwap.n_configured; ++dirn)
5d84beb5 677 SwapDirByIndex(dirn).openLog();
2745fea5
AR
678}
679
680void
681storeDirCloseSwapLogs()
682{
683 for (int dirn = 0; dirn < Config.cacheSwap.n_configured; ++dirn)
5d84beb5 684 SwapDirByIndex(dirn).closeLog();
2745fea5
AR
685}
686
024aeeee 687/**
2745fea5
AR
688 * storeDirWriteCleanLogs
689 *
690 * Writes a "clean" swap log file from in-memory metadata.
691 * This is a rewrite of the original function to troll each
692 * StoreDir and write the logs, and flush at the end of
693 * the run. Thanks goes to Eric Stern, since this solution
694 * came out of his COSS code.
695 */
696int
697storeDirWriteCleanLogs(int reopen)
698{
699 const StoreEntry *e = NULL;
700 int n = 0;
701
702 struct timeval start;
703 double dt;
2745fea5
AR
704 int dirn;
705 int notdone = 1;
706
707 // Check for store_dirs_rebuilding because fatal() often calls us in early
708 // initialization phases, before store log is initialized and ready. Also,
7c44eb08 709 // some stores do not support log cleanup during Store rebuilding.
2745fea5
AR
710 if (StoreController::store_dirs_rebuilding) {
711 debugs(20, DBG_IMPORTANT, "Not currently OK to rewrite swap log.");
712 debugs(20, DBG_IMPORTANT, "storeDirWriteCleanLogs: Operation aborted.");
713 return 0;
714 }
715
716 debugs(20, DBG_IMPORTANT, "storeDirWriteCleanLogs: Starting...");
717 getCurrentTime();
718 start = current_time;
719
720 for (dirn = 0; dirn < Config.cacheSwap.n_configured; ++dirn) {
5d84beb5 721 auto &sd = SwapDirByIndex(dirn);
2745fea5 722
5d84beb5
EB
723 if (sd.writeCleanStart() < 0) {
724 debugs(20, DBG_IMPORTANT, "log.clean.start() failed for dir #" << sd.index);
2745fea5
AR
725 continue;
726 }
727 }
728
729 /*
730 * This may look inefficient as CPU wise it is more efficient to do this
731 * sequentially, but I/O wise the parallellism helps as it allows more
732 * hdd spindles to be active.
733 */
734 while (notdone) {
735 notdone = 0;
736
737 for (dirn = 0; dirn < Config.cacheSwap.n_configured; ++dirn) {
5d84beb5 738 auto &sd = SwapDirByIndex(dirn);
2745fea5 739
5d84beb5 740 if (!sd.cleanLog)
2745fea5
AR
741 continue;
742
5d84beb5 743 e = sd.cleanLog->nextEntry();
2745fea5
AR
744
745 if (!e)
746 continue;
747
748 notdone = 1;
749
5d84beb5 750 if (!sd.canLog(*e))
2745fea5
AR
751 continue;
752
5d84beb5 753 sd.cleanLog->write(*e);
2745fea5
AR
754
755 if ((++n & 0xFFFF) == 0) {
756 getCurrentTime();
757 debugs(20, DBG_IMPORTANT, " " << std::setw(7) << n <<
758 " entries written so far.");
759 }
760 }
761 }
762
763 /* Flush */
764 for (dirn = 0; dirn < Config.cacheSwap.n_configured; ++dirn)
5d84beb5 765 SwapDirByIndex(dirn).writeCleanDone();
2745fea5
AR
766
767 if (reopen)
768 storeDirOpenSwapLogs();
769
770 getCurrentTime();
771
772 dt = tvSubDsec(start, current_time);
773
774 debugs(20, DBG_IMPORTANT, " Finished. Wrote " << n << " entries.");
775 debugs(20, DBG_IMPORTANT, " Took "<< std::setw(3)<< std::setprecision(2) << dt <<
776 " seconds ("<< std::setw(6) << ((double) n / (dt > 0.0 ? dt : 1.0)) << " entries/sec).");
777
778 return n;
779}
780
781/* Globals that should be converted to static Store::Disks methods */
782
783void
5d84beb5 784allocate_new_swapdir(Store::DiskConfig &swap)
2745fea5 785{
5d84beb5
EB
786 if (!swap.swapDirs) {
787 swap.n_allocated = 4;
788 swap.swapDirs = new SwapDir::Pointer[swap.n_allocated];
2745fea5
AR
789 }
790
5d84beb5
EB
791 if (swap.n_allocated == swap.n_configured) {
792 swap.n_allocated <<= 1;
793 const auto tmp = new SwapDir::Pointer[swap.n_allocated];
794 for (int i = 0; i < swap.n_configured; ++i) {
795 tmp[i] = swap.swapDirs[i];
b56b37cf 796 }
5d84beb5
EB
797 delete[] swap.swapDirs;
798 swap.swapDirs = tmp;
2745fea5
AR
799 }
800}
801
802void
803free_cachedir(Store::DiskConfig *swap)
804{
2745fea5
AR
805 /* DON'T FREE THESE FOR RECONFIGURE */
806
807 if (reconfiguring)
808 return;
809
b56b37cf
AJ
810 /* TODO XXX this lets the swapdir free resources asynchronously
811 * swap->swapDirs[i]->deactivate();
812 * but there may be such a means already.
813 * RBC 20041225
814 */
2745fea5 815
b56b37cf
AJ
816 // only free's the array memory itself
817 // the SwapDir objects may remain (ref-counted)
818 delete[] swap->swapDirs;
819 swap->swapDirs = nullptr;
2745fea5
AR
820 swap->n_allocated = 0;
821 swap->n_configured = 0;
822}
823
824/* Globals that should be moved to some Store::UFS-specific logging module */
825
024aeeee 826/**
2745fea5
AR
827 * An entry written to the swap log MUST have the following
828 * properties.
829 * 1. It MUST be a public key. It does no good to log
830 * a public ADD, change the key, then log a private
831 * DEL. So we need to log a DEL before we change a
832 * key from public to private.
833 * 2. It MUST have a valid (> -1) swap_filen.
834 */
835void
836storeDirSwapLog(const StoreEntry * e, int op)
837{
838 assert (e);
839 assert(!EBIT_TEST(e->flags, KEY_PRIVATE));
4310f8b0 840 assert(e->hasDisk());
2745fea5
AR
841 /*
842 * icons and such; don't write them to the swap log
843 */
844
845 if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
846 return;
847
848 assert(op > SWAP_LOG_NOP && op < SWAP_LOG_MAX);
849
850 debugs(20, 3, "storeDirSwapLog: " <<
851 swap_log_op_str[op] << " " <<
852 e->getMD5Text() << " " <<
853 e->swap_dirn << " " <<
854 std::hex << std::uppercase << std::setfill('0') << std::setw(8) << e->swap_filen);
855
5d84beb5 856 e->disk().logEntry(*e, op);
2745fea5 857}
7d84d4ca 858