Squid Web Cache master
Loading...
Searching...
No Matches
UFSSwapDir.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
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#define CLEAN_BUF_SZ 16384
12
13#include "squid.h"
14#include "base/IoManip.h"
15#include "base/Random.h"
16#include "cache_cf.h"
17#include "ConfigOption.h"
18#include "DiskIO/DiskIOModule.h"
20#include "fde.h"
21#include "FileMap.h"
22#include "fs_io.h"
23#include "globals.h"
24#include "Parsing.h"
25#include "RebuildState.h"
26#include "SquidConfig.h"
27#include "SquidMath.h"
28#include "StatCounters.h"
29#include "store_key_md5.h"
30#include "StoreSwapLogData.h"
31#include "tools.h"
32#include "UFSSwapDir.h"
33
34#include <cerrno>
35#include <cmath>
36#if HAVE_SYS_STAT_H
37#include <sys/stat.h>
38#endif
39
42
44{
45
46public:
47 UFSCleanLog(SwapDir *aSwapDir) : sd(aSwapDir) {}
48
50 const StoreEntry *nextEntry() override;
51
53 void write(StoreEntry const &) override;
54
58 char *outbuf = nullptr;
59 off_t outbuf_offset = 0;
60 int fd = -1;
62 SwapDir *sd = nullptr;
63};
64
65const StoreEntry *
67{
68 const StoreEntry *entry = nullptr;
69
70 if (walker)
71 entry = walker->Next(walker);
72
73 return entry;
74}
75
76void
78{
80 static size_t ss = sizeof(StoreSwapLogData);
81 s.op = (char) SWAP_LOG_ADD;
83 s.timestamp = e.timestamp;
84 s.lastref = e.lastref;
85 s.expires = e.expires;
86 s.lastmod = e.lastModified();
88 s.refcount = e.refcount;
89 s.flags = e.flags;
90 memcpy(&s.key, e.key, SQUID_MD5_DIGEST_LENGTH);
91 s.finalize();
92 memcpy(outbuf + outbuf_offset, &s, ss);
93 outbuf_offset += ss;
94 /* buffered write */
95
96 if (outbuf_offset + ss >= CLEAN_BUF_SZ) {
98 int xerrno = errno;
99 /* XXX This error handling should probably move up to the caller */
100 debugs(50, DBG_CRITICAL, MYNAME << newLog << ": write: " << xstrerr(xerrno));
101 debugs(50, DBG_CRITICAL, MYNAME << "Current swap logfile not replaced.");
102 file_close(fd);
103 fd = -1;
104 unlink(newLog.c_str());
105 sd->cleanLog = nullptr;
106 delete this;
107 return;
108 }
109
110 outbuf_offset = 0;
111 }
112}
113
114bool
115Fs::Ufs::UFSSwapDir::canStore(const StoreEntry &e, int64_t diskSpaceNeeded, int &load) const
116{
117 if (!SwapDir::canStore(e, diskSpaceNeeded, load))
118 return false;
119
120 if (IO->shedLoad())
121 return false;
122
123 load = IO->load();
124 return true;
125}
126
127static void
128FreeObject(void *address)
129{
130 StoreSwapLogData *anObject = static_cast <StoreSwapLogData *>(address);
131 delete anObject;
132}
133
134static int
135rev_int_sort(const void *A, const void *B)
136{
137 const int *i1 = (const int *)A;
138 const int *i2 = (const int *)B;
139 return *i2 - *i1;
140}
141
142void
144{
145 int i = GetInteger();
146 if (i <= 0)
147 fatal("UFSSwapDir::parseSizeL1L2: invalid size value");
148
149 const uint64_t size = static_cast<uint64_t>(i) << 20; // MBytes to Bytes
150
151 /* just reconfigure it */
152 if (reconfiguring) {
153 if (size == maxSize())
154 debugs(3, 2, "Cache dir '" << path << "' size remains unchanged at " << i << " MB");
155 else
156 debugs(3, DBG_IMPORTANT, "Cache dir '" << path << "' size changed to " << i << " MB");
157 }
158
159 max_size = size;
160
161 l1 = GetInteger();
162
163 if (l1 <= 0)
164 fatal("UFSSwapDir::parseSizeL1L2: invalid level 1 directories value");
165
166 l2 = GetInteger();
167
168 if (l2 <= 0)
169 fatal("UFSSwapDir::parseSizeL1L2: invalid level 2 directories value");
170}
171
172void
174{
175 parseSizeL1L2();
176 parseOptions(1);
177}
178
179void
180Fs::Ufs::UFSSwapDir::parse (int anIndex, char *aPath)
181{
182 index = anIndex;
183 path = xstrdup(aPath);
184
185 parseSizeL1L2();
186
187 /* Initialise replacement policy stuff */
189
190 parseOptions(0);
191}
192
193void
195{
196 DiskIOStrategy *anIO = module->createStrategy();
197 safe_free(ioType);
198 ioType = xstrdup(module->type());
199
200 delete IO->io;
201 IO->io = anIO;
202 /* Change the IO Options */
203
204 if (currentIOOptions && currentIOOptions->options.size() > 2) {
205 delete currentIOOptions->options.back();
206 currentIOOptions->options.pop_back();
207 }
208
209 /* TODO: factor out these 4 lines */
210 ConfigOption *ioOptions = IO->io->getOptionTree();
211
212 if (currentIOOptions && ioOptions)
213 currentIOOptions->options.push_back(ioOptions);
214}
215
216bool
217Fs::Ufs::UFSSwapDir::optionIOParse(char const *option, const char *value, int isaReconfig)
218{
219 if (strcmp(option, "IOEngine") != 0)
220 return false;
221
222 if (isaReconfig)
223 /* silently ignore this */
224 return true;
225
226 if (!value) {
228 return false;
229 }
230
231 DiskIOModule *module = DiskIOModule::Find(value);
232
233 if (!module) {
235 return false;
236 }
237
238 changeIO(module);
239
240 return true;
241}
242
243void
245{
246 storeAppendPrintf(e, " IOEngine=%s", ioType);
247}
248
251{
252 ConfigOption *parentResult = SwapDir::getOptionTree();
253
254 if (currentIOOptions == nullptr)
255 currentIOOptions = new ConfigOptionVector();
256
257 currentIOOptions->options.push_back(parentResult);
258
259 currentIOOptions->options.push_back(new ConfigOptionAdapter<UFSSwapDir>(*const_cast<UFSSwapDir *>(this), &UFSSwapDir::optionIOParse, &UFSSwapDir::optionIODump));
260
261 if (ConfigOption *ioOptions = IO->io->getOptionTree())
262 currentIOOptions->options.push_back(ioOptions);
263
264 ConfigOption* result = currentIOOptions;
265
266 currentIOOptions = nullptr;
267
268 return result;
269}
270
271void
273{
274 debugs(47, 3, "Initialising UFS SwapDir engine.");
275 /* Parsing must be finished by now - force to NULL, don't delete */
276 currentIOOptions = nullptr;
277 static int started_clean_event = 0;
278 static const char *errmsg =
279 "\tFailed to verify one of the swap directories, Check cache.log\n"
280 "\tfor details. Run 'squid -z' to create swap directories\n"
281 "\tif needed, or if running Squid for the first time.";
282 IO->init();
283
284 if (verifyCacheDirs())
285 fatal(errmsg);
286
287 openLog();
288
289 rebuild();
290
291 if (!started_clean_event) {
292 eventAdd("UFS storeDirClean", CleanEvent, nullptr, 15.0, 1);
293 started_clean_event = 1;
294 }
295
296 (void) fsBlockSize(path, &fs.blksize);
297}
298
299void
301{
302 debugs(47, 3, "Creating swap space in " << path);
303 createDirectory(path, 0);
304 createSwapSubDirs();
305}
306
307Fs::Ufs::UFSSwapDir::UFSSwapDir(char const *aType, const char *anIOType) :
308 SwapDir(aType),
309 IO(nullptr),
310 fsdata(nullptr),
311 map(new FileMap()),
312 suggest(0),
313 l1(16),
314 l2(256),
315 swaplog_fd(-1),
316 currentIOOptions(new ConfigOptionVector()),
317 ioType(xstrdup(anIOType)),
318 cur_size(0),
320 rebuilding_(false)
321{
322 /* modulename is only set to disk modules that are built, by configure,
323 * so the Find call should never return NULL here.
324 */
325 IO = new Fs::Ufs::UFSStrategy(DiskIOModule::Find(anIOType)->createStrategy());
326}
327
329{
330 if (swaplog_fd > -1) {
331 file_close(swaplog_fd);
332 swaplog_fd = -1;
333 }
334 xfree(ioType);
335 delete map;
336 delete IO;
337 delete currentIOOptions;
338}
339
340void
342{
343 debugs(47, DBG_CRITICAL, "FILENO " << asHex(e.swap_filen).upperCase().minDigits(8) <<
344 ", PATH " << fullPath(e.swap_filen, nullptr));
345 e.dump(0);
346}
347
348bool
350{
351
352 struct stat sb;
353
354 if (::stat(fullPath(e.swap_filen, nullptr), &sb) < 0) {
355 debugs(47, DBG_CRITICAL, "WARNING: Missing swap file");
356 dumpEntry(e);
357 return true;
358 }
359
360 if ((off_t)e.swap_file_sz != sb.st_size) {
361 debugs(47, DBG_CRITICAL, "WARNING: Size Mismatch. Entry size: "
362 << e.swap_file_sz << ", file size: " << sb.st_size);
363 dumpEntry(e);
364 return true;
365 }
366
367 return false;
368}
369
370void
372{
373 int totl_kb = 0;
374 int free_kb = 0;
375 int totl_in = 0;
376 int free_in = 0;
377 int x;
378 storeAppendPrintf(&sentry, "First level subdirectories: %d\n", l1);
379 storeAppendPrintf(&sentry, "Second level subdirectories: %d\n", l2);
380 storeAppendPrintf(&sentry, "Maximum Size: %" PRIu64 " KB\n", maxSize() >> 10);
381 storeAppendPrintf(&sentry, "Current Size: %.2f KB\n", currentSize() / 1024.0);
382 storeAppendPrintf(&sentry, "Percent Used: %0.2f%%\n",
383 Math::doublePercent(currentSize(), maxSize()));
384 storeAppendPrintf(&sentry, "Filemap bits in use: %d of %d (%d%%)\n",
385 map->numFilesInMap(), map->capacity(),
386 Math::intPercent(map->numFilesInMap(), map->capacity()));
387 x = fsStats(path, &totl_kb, &free_kb, &totl_in, &free_in);
388
389 if (0 == x) {
390 storeAppendPrintf(&sentry, "Filesystem Space in use: %d/%d KB (%d%%)\n",
391 totl_kb - free_kb,
392 totl_kb,
393 Math::intPercent(totl_kb - free_kb, totl_kb));
394 storeAppendPrintf(&sentry, "Filesystem Inodes in use: %d/%d (%d%%)\n",
395 totl_in - free_in,
396 totl_in,
397 Math::intPercent(totl_in - free_in, totl_in));
398 }
399
400 storeAppendPrintf(&sentry, "Flags:");
401
402 if (flags.selected)
403 storeAppendPrintf(&sentry, " SELECTED");
404
405 if (flags.read_only)
406 storeAppendPrintf(&sentry, " READ-ONLY");
407
408 storeAppendPrintf(&sentry, "\n");
409
410 IO->statfs(sentry);
411}
412
413void
415{
416 /* TODO: possible options for improvement;
417 *
418 * Note that too much aggression here is not good. It means that disk
419 * controller is getting a long queue of removals to act on, along
420 * with its regular I/O queue, and that client traffic is 'paused'
421 * and growing the network I/O queue as well while the scan happens.
422 * Possibly bad knock-on effects as Squid catches up on all that.
423 *
424 * Bug 2448 may have been a sign of what can wrong. At the least it
425 * provides a test case for aggression effects in overflow conditions.
426 *
427 * - base removal limit on space saved, instead of count ?
428 *
429 * - base removal rate on a traffic speed counter ?
430 * as the purge took up more time out of the second it would grow to
431 * a graceful full pause
432 *
433 * - pass out a value to cause another event to be scheduled immediately
434 * instead of waiting a whole second more ?
435 * knock on; schedule less if all caches are under low-water
436 *
437 * - admin configurable removal rate or count ?
438 * the current numbers are arbitrary, config helps with experimental
439 * trials and future-proofing the install base.
440 * we also have this indirectly by shifting the relative positions
441 * of low-, high- water and the total capacity limit.
442 */
443
444 // minSize() is swap_low_watermark in bytes
445 const uint64_t lowWaterSz = minSize();
446
447 if (currentSize() < lowWaterSz) {
448 debugs(47, 5, "space still available in " << path);
449 return;
450 }
451
452 /* We can't delete objects while rebuilding swap */
453 /* XXX each store should start maintaining as it comes online. */
455 // suppress the warnings, except once each minute
456 static int64_t lastWarn = 0;
457 int warnLevel = 3;
458 if (lastWarn+60 < squid_curtime) {
459 lastWarn = squid_curtime;
460 warnLevel = DBG_IMPORTANT;
461 }
462 debugs(47, warnLevel, StoreController::store_dirs_rebuilding << " cache_dir still rebuilding. Skip GC for " << path);
463 return;
464 }
465
466 // maxSize() is cache_dir total size in bytes
467 const uint64_t highWaterSz = ((maxSize() * Config.Swap.highWaterMark) / 100);
468
469 // f is percentage of 'gap' filled between low- and high-water.
470 // Used to reduced purge rate when between water markers, and
471 // to multiply it more aggressively the further above high-water
472 // it reaches. But in a graceful linear growth curve.
473 double f = 1.0;
474 if (highWaterSz > lowWaterSz) {
475 // might be equal. n/0 is bad.
476 f = (double) (currentSize() - lowWaterSz) / (highWaterSz - lowWaterSz);
477 }
478
479 // how deep to look for a single object that can be removed
480 int max_scan = (int) (f * 400.0 + 100.0);
481
482 // try to purge only this many objects this cycle.
483 int max_remove = (int) (f * 300.0 + 20.0);
484
485 /*
486 * This is kinda cheap, but so we need this priority hack?
487 */
488 debugs(47, 3, "f=" << f << ", max_scan=" << max_scan << ", max_remove=" << max_remove);
489
490 RemovalPurgeWalker *walker = repl->PurgeInit(repl, max_scan);
491
492 int removed = 0;
493 // only purge while above low-water
494 while (currentSize() >= lowWaterSz) {
495
496 // stop if we reached max removals for this cycle,
497 // Bug 2448 may be from this not clearing enough,
498 // but it predates the current algorithm so not sure
499 if (removed >= max_remove)
500 break;
501
502 StoreEntry *e = walker->Next(walker);
503
504 // stop if all objects are locked / in-use,
505 // or the cache is empty
506 if (!e)
507 break; /* no more objects */
508
509 ++removed;
510
511 e->release(true);
512 }
513
514 walker->Done(walker);
515 debugs(47, (removed ? 2 : 3), path <<
516 " removed " << removed << "/" << max_remove << " f=" <<
517 std::setprecision(4) << f << " max_scan=" << max_scan);
518
519 // what if cache is still over the high watermark ?
520 // Store::Maintain() schedules another purge in 1 second.
521}
522
523void
525{
526 debugs(47, 3, "referencing " << &e << " " <<
527 e.swap_dirn << "/" << e.swap_filen);
528
529 if (repl->Referenced)
530 repl->Referenced(repl, &e, &e.repl);
531}
532
533bool
535{
536 debugs(47, 3, "dereferencing " << &e << " " <<
537 e.swap_dirn << "/" << e.swap_filen);
538
539 if (repl->Dereferenced)
540 repl->Dereferenced(repl, &e, &e.repl);
541
542 return true; // keep e in the global store_table
543}
544
546Fs::Ufs::UFSSwapDir::createStoreIO(StoreEntry &e, StoreIOState::STIOCB * const aCallback, void * const callback_data)
547{
548 return IO->create(this, &e, aCallback, callback_data);
549}
550
552Fs::Ufs::UFSSwapDir::openStoreIO(StoreEntry &e, StoreIOState::STIOCB * const aCallback, void * const callback_data)
553{
554 return IO->open(this, &e, aCallback, callback_data);
555}
556
557int
559{
560 return map->testBit(filn);
561}
562
563void
565{
566 map->setBit(filn);
567}
568
569void
571{
572 /*
573 * We have to test the bit before calling clearBit as
574 * it doesn't do bounds checking and blindly assumes
575 * filn is a valid file number, but it might not be because
576 * the map is dynamic in size. Also clearing an already clear
577 * bit puts the map counter of-of-whack.
578 */
579
580 if (map->testBit(filn))
581 map->clearBit(filn);
582}
583
584int
586{
587 int fn;
588 fn = map->allocate(suggest);
589 map->setBit(fn);
590 suggest = fn + 1;
591 return fn;
592}
593
594char *
596{
597 LOCAL_ARRAY(char, fullfilename, MAXPATHLEN);
598 assert(0 <= subdirn && subdirn < l1);
599 snprintf(fullfilename, MAXPATHLEN, "%s/%02X", path, subdirn);
600 return fullfilename;
601}
602
603int
604Fs::Ufs::UFSSwapDir::createDirectory(const char *aPath, int should_exist)
605{
606 int created = 0;
607
608 struct stat st;
610
611 if (0 == ::stat(aPath, &st)) {
612 if (S_ISDIR(st.st_mode)) {
613 debugs(47, (should_exist ? 3 : DBG_IMPORTANT), aPath << " exists");
614 } else {
615 fatalf("Swap directory %s is not a directory.", aPath);
616 }
617 } else if (0 == mkdir(aPath, 0750)) {
618 debugs(47, (should_exist ? DBG_IMPORTANT : 3), aPath << " created");
619 created = 1;
620 } else {
621 int xerrno = errno;
622 fatalf("Failed to make swap directory %s: %s", aPath, xstrerr(xerrno));
623 }
624
625 return created;
626}
627
628bool
630{
631
632 struct stat sb;
633
634 if (::stat(aPath, &sb) < 0) {
635 int xerrno = errno;
636 debugs(47, DBG_CRITICAL, "ERROR: " << aPath << ": " << xstrerr(xerrno));
637 return false;
638 }
639
640 if (S_ISDIR(sb.st_mode) == 0) {
641 debugs(47, DBG_CRITICAL, "WARNING: " << aPath << " is not a directory");
642 return false;
643 }
644
645 return true;
646}
647
648bool
650{
651 if (!pathIsDirectory(path))
652 return true;
653
654 for (int j = 0; j < l1; ++j) {
655 char const *aPath = swapSubDir(j);
656
657 if (!pathIsDirectory(aPath))
658 return true;
659 }
660
661 return false;
662}
663
664void
666{
667 LOCAL_ARRAY(char, name, MAXPATHLEN);
668
669 for (int i = 0; i < l1; ++i) {
670 snprintf(name, MAXPATHLEN, "%s/%02X", path, i);
671
672 int should_exist;
673
674 if (createDirectory(name, 0))
675 should_exist = 0;
676 else
677 should_exist = 1;
678
679 debugs(47, DBG_IMPORTANT, "Making directories in " << name);
680
681 for (int k = 0; k < l2; ++k) {
682 snprintf(name, MAXPATHLEN, "%s/%02X/%02X", path, i, k);
683 createDirectory(name, should_exist);
684 }
685 }
686}
687
688SBuf
689Fs::Ufs::UFSSwapDir::logFile(char const *ext) const
690{
691 SBuf lpath;
692
693 if (Config.Log.swap) {
694 static char pathtmp[MAXPATHLEN];
695 char *pathtmp2 = xstrncpy(pathtmp, path, MAXPATHLEN - 64);
696
697 // replace all '/' with '.'
698 while ((pathtmp2 = strchr(pathtmp2, '/')))
699 *pathtmp2 = '.';
700
701 // remove any trailing '.' characters
702 int pos = strlen(pathtmp);
703 while (pos && pathtmp[pos-1] == '.')
704 pathtmp[--pos] = '\0';
705
706 // remove any prefix '.' characters
707 for (pathtmp2 = pathtmp; *pathtmp2 == '.'; ++pathtmp2);
708 // replace a '%s' (if any) in the config string
709 // with the resulting pathtmp2 string
710 lpath.appendf(Config.Log.swap, pathtmp2);
711
712 // is pathtmp2 was NOT injected, append numeric file extension
713 if (lpath.cmp(Config.Log.swap) == 0) {
714 lpath.append(".", 1);
715 lpath.appendf("%02d", index);
716 }
717 } else {
718 lpath.append(path);
719 lpath.append("/swap.state", 11);
720 }
721
722 lpath.append(ext); // may be nil, that is okay.
723
724 return lpath;
725}
726
727void
729{
730 if (!IamWorkerProcess())
731 return;
732
733 assert(NumberOfUFSDirs || !UFSDirToGlobalDirMapping);
734 ++NumberOfUFSDirs;
735 assert(NumberOfUFSDirs <= Config.cacheSwap.n_configured);
736
737 if (rebuilding_) { // we did not close the temporary log used for rebuilding
738 assert(swaplog_fd >= 0);
739 return;
740 }
741
742 SBuf logPath(logFile());
743 swaplog_fd = file_open(logPath.c_str(), O_WRONLY | O_CREAT | O_BINARY);
744
745 if (swaplog_fd < 0) {
746 int xerrno = errno;
747 debugs(50, DBG_IMPORTANT, "ERROR: opening swap log " << logPath << ": " << xstrerr(xerrno));
748 fatal("UFSSwapDir::openLog: Failed to open swap log.");
749 }
750
751 debugs(50, 3, "Cache Dir #" << index << " log opened on FD " << swaplog_fd);
752}
753
754void
756{
757 if (swaplog_fd < 0) /* not open */
758 return;
759
760 assert(NumberOfUFSDirs > 0);
761 --NumberOfUFSDirs;
762 if (!NumberOfUFSDirs)
763 safe_free(UFSDirToGlobalDirMapping);
764
765 if (rebuilding_) // we cannot close the temporary log used for rebuilding
766 return;
767
768 file_close(swaplog_fd);
769
770 debugs(47, 3, "Cache Dir #" << index << " log closed on FD " << swaplog_fd);
771
772 swaplog_fd = -1;
773}
774
775bool
777{
778 return anInt < l1;
779}
780
781bool
783{
784 return anInt < l2;
785}
786
789 sfileno file_number,
790 uint64_t swap_file_sz,
791 time_t expires,
792 time_t timestamp,
793 time_t lastref,
794 time_t lastmod,
795 uint32_t refcount,
796 uint16_t newFlags,
797 int)
798{
799 StoreEntry *e = nullptr;
800 debugs(47, 5, storeKeyText(key) << ", fileno=" << asHex(file_number).upperCase().minDigits(8));
801 /* if you call this you'd better be sure file_number is not
802 * already in use! */
803 e = new StoreEntry();
806 e->attachToDisk(index, file_number, SWAPOUT_DONE);
807 e->swap_file_sz = swap_file_sz;
808 e->lastref = lastref;
809 e->timestamp = timestamp;
810 e->expires = expires;
811 e->lastModified(lastmod);
812 e->refcount = refcount;
813 e->flags = newFlags;
816 mapBitSet(e->swap_filen);
817 cur_size += fs.blksize * sizeInBlocks(e->swap_file_sz);
819 e->hashInsert(key);
820 replacementAdd (e);
821 return e;
822}
823
824void
829
830void
832{
833 assert(rebuilding_);
834 rebuilding_ = false;
835
836 SBuf swaplog_path(logFile()); // where the swaplog should be
837 SBuf tmp_path(logFile(".new"));
838
839 file_close(swaplog_fd);
840
841 if (!FileRename(tmp_path, swaplog_path)) {
842 fatalf("Failed to rename log file " SQUIDSBUFPH " to " SQUIDSBUFPH, SQUIDSBUFPRINT(tmp_path), SQUIDSBUFPRINT(swaplog_path));
843 }
844
845 int fd = file_open(swaplog_path.c_str(), O_WRONLY | O_CREAT | O_BINARY);
846
847 if (fd < 0) {
848 int xerrno = errno;
849 debugs(50, DBG_IMPORTANT, "ERROR: " << swaplog_path << ": " << xstrerr(xerrno));
850 fatalf("Failed to open swap log " SQUIDSBUFPH, SQUIDSBUFPRINT(swaplog_path));
851 }
852
853 swaplog_fd = fd;
854 debugs(47, 3, "Cache Dir #" << index << " log opened on FD " << fd);
855}
856
857FILE *
858Fs::Ufs::UFSSwapDir::openTmpSwapLog(int *clean_flag, int *zero_flag)
859{
860 assert(!rebuilding_);
861
862 SBuf swaplog_path(logFile());
863 SBuf clean_path(logFile(".last-clean"));
864 SBuf new_path(logFile(".new"));
865
866 struct stat log_sb;
867
868 struct stat clean_sb;
869
870 if (::stat(swaplog_path.c_str(), &log_sb) < 0) {
871 debugs(47, DBG_IMPORTANT, "Cache Dir #" << index << ": No log file");
872 return nullptr;
873 }
874
875 *zero_flag = log_sb.st_size == 0 ? 1 : 0;
876 /* close the existing write-only FD */
877
878 if (swaplog_fd >= 0)
879 file_close(swaplog_fd);
880
881 /* open a write-only FD for the new log */
882 int fd = file_open(new_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
883 if (fd < 0) {
884 int xerrno = errno;
885 debugs(50, DBG_IMPORTANT, "ERROR: while opening swap log" << new_path << ": " << xstrerr(xerrno));
886 fatalf("Failed to open swap log " SQUIDSBUFPH, SQUIDSBUFPRINT(new_path));
887 }
888
889 swaplog_fd = fd;
890 rebuilding_ = true;
891
892 {
893 const StoreSwapLogHeader header;
894 MemBuf buf;
895 buf.init(header.record_size, header.record_size);
896 buf.append(reinterpret_cast<const char*>(&header), sizeof(header));
897 // Pad to keep in sync with UFSSwapDir::writeCleanStart().
898 memset(buf.space(), 0, header.gapSize());
899 buf.appended(header.gapSize());
900 file_write(swaplog_fd, -1, buf.content(), buf.contentSize(),
901 nullptr, nullptr, buf.freeFunc());
902 }
903
904 /* open a read-only stream of the old log */
905 FILE *fp = fopen(swaplog_path.c_str(), "rb");
906 if (!fp) {
907 int xerrno = errno;
908 debugs(50, DBG_CRITICAL, "ERROR: while opening " << swaplog_path << ": " << xstrerr(xerrno));
909 fatalf("Failed to open swap log for reading " SQUIDSBUFPH, SQUIDSBUFPRINT(swaplog_path));
910 }
911
912 memset(&clean_sb, '\0', sizeof(struct stat));
913
914 if (::stat(clean_path.c_str(), &clean_sb) < 0)
915 *clean_flag = 0;
916 else if (clean_sb.st_mtime < log_sb.st_mtime)
917 *clean_flag = 0;
918 else
919 *clean_flag = 1;
920
921 safeunlink(clean_path.c_str(), 1);
922
923 return fp;
924}
925
926/*
927 * Begin the process to write clean cache state. For AUFS this means
928 * opening some log files and allocating write buffers. Return 0 if
929 * we succeed, and assign the 'func' and 'data' return pointers.
930 */
931int
933{
934 UFSCleanLog *state = new UFSCleanLog(this);
935 StoreSwapLogHeader header;
936#if HAVE_FCHMOD
937
938 struct stat sb;
939#endif
940
941 cleanLog = nullptr;
942 state->cur = logFile();
943 state->newLog = logFile(".clean");
944 state->fd = file_open(state->newLog.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
945
946 if (state->fd < 0) {
947 delete state;
948 return -1;
949 }
950
951 state->cln = state->cur;
952 state->cln.append(".last-clean");
953 state->outbuf = (char *)xcalloc(CLEAN_BUF_SZ, 1);
954 state->outbuf_offset = 0;
955 /*copy the header */
956 memcpy(state->outbuf, &header, sizeof(StoreSwapLogHeader));
957 // Leave a gap to keep in sync with UFSSwapDir::openTmpSwapLog().
958 memset(state->outbuf + sizeof(StoreSwapLogHeader), 0, header.gapSize());
959 state->outbuf_offset += header.record_size;
960
961 state->walker = repl->WalkInit(repl);
962 ::unlink(state->cln.c_str());
963 debugs(47, 3, "opened " << state->newLog << ", FD " << state->fd);
964#if HAVE_FCHMOD
965
966 if (::stat(state->cur.c_str(), &sb) == 0)
967 fchmod(state->fd, sb.st_mode);
968
969#endif
970
971 cleanLog = state;
972 return 0;
973}
974
975void
977{
978 UFSCleanLog *state = (UFSCleanLog *)cleanLog;
979 int fd;
980
981 if (nullptr == state)
982 return;
983
984 if (state->fd < 0)
985 return;
986
987 state->walker->Done(state->walker);
988
989 if (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) {
990 int xerrno = errno;
991 debugs(50, DBG_CRITICAL, MYNAME << state->newLog << ": write: " << xstrerr(xerrno));
992 debugs(50, DBG_CRITICAL, MYNAME << "Current swap logfile not replaced.");
993 file_close(state->fd);
994 state->fd = -1;
995 ::unlink(state->newLog.c_str());
996 }
997
998 safe_free(state->outbuf);
999 /*
1000 * You can't rename open files on Microsoft "operating systems"
1001 * so we have to close before renaming.
1002 */
1003 closeLog();
1004 /* save the fd value for a later test */
1005 fd = state->fd;
1006 /* rename */
1007
1008 if (state->fd >= 0) {
1009#if _SQUID_OS2_ || _SQUID_WINDOWS_
1010 file_close(state->fd);
1011 state->fd = -1;
1012#endif
1013
1014 FileRename(state->newLog, state->cur);
1015 // TODO handle rename errors
1016 }
1017
1018 /* touch a timestamp file if we're not still validating */
1020 (void) 0;
1021 else if (fd < 0)
1022 (void) 0;
1023 else
1024 file_close(file_open(state->cln.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY));
1025
1026 /* close */
1027 if (state->fd >= 0)
1028 file_close(state->fd);
1029
1030 state->fd = -1;
1031
1032 delete state;
1033
1034 cleanLog = nullptr;
1035}
1036
1038int
1040{
1041 static int swap_index = 0;
1042 int j = 0;
1043 size_t n = 0;
1044
1045 if (!NumberOfUFSDirs)
1046 return 0; // probably in the middle of reconfiguration
1047
1048 if (nullptr == UFSDirToGlobalDirMapping) {
1049 SwapDir *sd;
1050 /*
1051 * Initialize the little array that translates UFS cache_dir
1052 * number into the Config.cacheSwap.swapDirs array index.
1053 */
1054 UFSDirToGlobalDirMapping = (int *)xcalloc(NumberOfUFSDirs, sizeof(*UFSDirToGlobalDirMapping));
1055
1056 for (size_t i = 0; i < Config.cacheSwap.n_configured; ++i) {
1057 /* This is bogus, the controller should just clean each instance once */
1058 sd = dynamic_cast <SwapDir *>(INDEXSD(i));
1059
1060 if (!UFSSwapDir::IsUFSDir(sd))
1061 continue;
1062
1063 UFSSwapDir *usd = dynamic_cast<UFSSwapDir *>(sd);
1064
1065 assert (usd);
1066
1067 UFSDirToGlobalDirMapping[n] = i;
1068 ++n;
1069
1070 j += (usd->l1 * usd->l2);
1071 }
1072
1073 assert(n == NumberOfUFSDirs);
1074 /*
1075 * Start the commonUfsDirClean() swap_index with a random
1076 * value. j equals the total number of UFS level 2
1077 * swap directories
1078 */
1079 static std::mt19937 mt(RandomSeed32());
1080 std::uniform_int_distribution<> dist(0, j);
1081 swap_index = dist(mt);
1082 }
1083
1084 /* if the rebuild is finished, start cleaning directories. */
1086 n = DirClean(swap_index);
1087 ++swap_index;
1088 }
1089
1090 return n;
1091}
1092
1093void
1095{
1096 const int n = HandleCleanEvent();
1097 eventAdd("storeDirClean", CleanEvent, nullptr,
1098 15.0 * exp(-0.25 * n), 1);
1099}
1100
1101bool
1103{
1104 UFSSwapDir *mySD = dynamic_cast<UFSSwapDir *>(sd);
1105 return (mySD != nullptr) ;
1106}
1107
1108/*
1109 * XXX: this is broken - it assumes all cache dirs use the same
1110 * l1 and l2 scheme. -RBC 20021215. Partial fix is in place -
1111 * if not UFSSwapDir return 0;
1112 */
1113bool
1115{
1116 int D1, D2;
1117 int L1, L2;
1118 int filn = fn;
1119 assert(F0 >= 0);
1120 assert(static_cast<size_t>(F0) < Config.cacheSwap.n_configured);
1121 assert (UFSSwapDir::IsUFSDir (dynamic_cast<SwapDir *>(INDEXSD(F0))));
1122 UFSSwapDir *sd = dynamic_cast<UFSSwapDir *>(INDEXSD(F0));
1123
1124 if (!sd)
1125 return 0;
1126
1127 L1 = sd->l1;
1128
1129 L2 = sd->l2;
1130
1131 D1 = ((filn / L2) / L2) % L1;
1132
1133 if (F1 != D1)
1134 return 0;
1135
1136 D2 = (filn / L2) % L2;
1137
1138 if (F2 != D2)
1139 return 0;
1140
1141 return 1;
1142}
1143
1144int
1146{
1147 if (filn < 0)
1148 return 0;
1149
1150 /*
1151 * If flag is set it means out-of-range file number should
1152 * be considered invalid.
1153 */
1154 if (flag)
1155 if (filn > map->capacity())
1156 return 0;
1157
1158 return 1;
1159}
1160
1161void
1163{
1164 debugs(79, 3, "unlinking fileno " << asHex(f).upperCase().minDigits(8) << " '" <<
1165 fullPath(f,nullptr) << "'");
1166 /* commonUfsDirMapBitReset(this, f); */
1167 IO->unlinkFile(fullPath(f,nullptr));
1168}
1169
1170bool
1172{
1173 // unlinkd may be useful only in workers
1174 return IamWorkerProcess() && IO->io->unlinkdUseful();
1175}
1176
1177void
1179{
1180 debugs(79, 3, e);
1181 if (e.locked()) // somebody else may still be using this file
1182 return; // nothing to do: our get() always returns nil
1183
1184 if (!e.hasDisk())
1185 return; // see evictIfFound()
1186
1187 // Since these fields grow only after swap out ends successfully,
1188 // do not decrement them for e.swappingOut() and e.swapoutFailed().
1189 if (e.swappedOut()) {
1190 cur_size -= fs.blksize * sizeInBlocks(e.swap_file_sz);
1192 }
1193 replacementRemove(&e);
1194 mapBitReset(e.swap_filen);
1196 e.detachFromDisk();
1197}
1198
1199void
1201{
1202 // UFS disk entries always have (attached) StoreEntries so if we got here,
1203 // the entry is not cached on disk and there is nothing for us to do.
1204}
1205
1206void
1208{
1209 debugs(47, 4, "added node " << e << " to dir " << index);
1210 repl->Add(repl, e, &e->repl);
1211}
1212
1213void
1215{
1216 assert(e->hasDisk());
1217
1219
1220 assert (dynamic_cast<UFSSwapDir *>(SD.getRaw()) == this);
1221
1222 debugs(47, 4, "remove node " << e << " from dir " << index);
1223
1224 repl->Remove(repl, e, &e->repl);
1225}
1226
1227void
1229{
1230 storeAppendPrintf(&entry, " %" PRIu64 " %d %d", maxSize() >> 20, l1, l2);
1231 dumpOptions(&entry);
1232}
1233
1234char *
1235Fs::Ufs::UFSSwapDir::fullPath(sfileno filn, char *fullpath) const
1236{
1237 LOCAL_ARRAY(char, fullfilename, MAXPATHLEN);
1238 int L1 = l1;
1239 int L2 = l2;
1240
1241 if (!fullpath)
1242 fullpath = fullfilename;
1243
1244 fullpath[0] = '\0';
1245
1246 snprintf(fullpath, MAXPATHLEN, "%s/%02X/%02X/%08X",
1247 path,
1248 ((filn / L2) / L2) % L1,
1249 (filn / L2) % L2,
1250 filn);
1251
1252 return fullpath;
1253}
1254
1255int
1257{
1258 return IO->callback();
1259}
1260
1261void
1263{
1264 IO->sync();
1265}
1266
1267void
1269{
1270 cur_size += fs.blksize * sizeInBlocks(e.swap_file_sz);
1272}
1273
1274void
1276{
1277 debugs(47, 5, entry);
1278 // rely on the expected eventual StoreEntry::release(), evictCached(), or
1279 // a similar call to call unlink(), detachFromDisk(), etc. for the entry.
1280}
1281
1282void
1284{
1285 if (swaplog_fd < 0) {
1286 debugs(36, 5, "cannot log " << e << " in the middle of reconfiguration");
1287 return;
1288 }
1289
1291 s->op = (char) op;
1292 s->swap_filen = e.swap_filen;
1293 s->timestamp = e.timestamp;
1294 s->lastref = e.lastref;
1295 s->expires = e.expires;
1296 s->lastmod = e.lastModified();
1298 s->refcount = e.refcount;
1299 s->flags = e.flags;
1300 memcpy(s->key, e.key, SQUID_MD5_DIGEST_LENGTH);
1301 s->finalize();
1302 file_write(swaplog_fd,
1303 -1,
1304 s,
1305 sizeof(StoreSwapLogData),
1306 nullptr,
1307 nullptr,
1308 FreeObject);
1309}
1310
1311int
1313{
1314 DIR *dir_pointer = nullptr;
1315 int files[20];
1316 int swapfileno;
1317 int fn; /* same as swapfileno, but with dirn bits set */
1318 int n = 0;
1319 int k = 0;
1320 int N0, N1, N2;
1321 int D0, D1, D2;
1322 UFSSwapDir *SD;
1323 N0 = NumberOfUFSDirs;
1324 D0 = UFSDirToGlobalDirMapping[swap_index % N0];
1325 SD = dynamic_cast<UFSSwapDir *>(INDEXSD(D0));
1326 assert (SD);
1327 N1 = SD->l1;
1328 D1 = (swap_index / N0) % N1;
1329 N2 = SD->l2;
1330 D2 = ((swap_index / N0) / N1) % N2;
1331
1332 SBuf p1;
1333 p1.appendf("%s/%02X/%02X", SD->path, D1, D2);
1334 debugs(36, 3, "Cleaning directory " << p1);
1335 dir_pointer = opendir(p1.c_str());
1336
1337 if (!dir_pointer) {
1338 int xerrno = errno;
1339 if (xerrno == ENOENT) {
1340 debugs(36, DBG_CRITICAL, MYNAME << "WARNING: Creating " << p1);
1341 if (mkdir(p1.c_str(), 0750) == 0)
1342 return 0;
1343 }
1344
1345 debugs(50, DBG_CRITICAL, MYNAME << p1 << ": " << xstrerr(xerrno));
1346 safeunlink(p1.c_str(), 1);
1347 return 0;
1348 }
1349
1350 dirent_t *de;
1351 while ((de = readdir(dir_pointer)) != nullptr && k < 20) {
1352 if (sscanf(de->d_name, "%X", &swapfileno) != 1)
1353 continue;
1354
1355 fn = swapfileno; /* XXX should remove this cruft ! */
1356
1357 if (SD->validFileno(fn, 1))
1358 if (SD->mapBitTest(fn))
1359 if (UFSSwapDir::FilenoBelongsHere(fn, D0, D1, D2))
1360 continue;
1361
1362 files[k] = swapfileno;
1363 ++k;
1364 }
1365
1366 closedir(dir_pointer);
1367
1368 if (k == 0)
1369 return 0;
1370
1371 qsort(files, k, sizeof(int), rev_int_sort);
1372
1373 if (k > 10)
1374 k = 10;
1375
1376 for (n = 0; n < k; ++n) {
1377 debugs(36, 3, "Cleaning file " << asHex(files[n]).upperCase().minDigits(8));
1378 SBuf p2(p1);
1379 p2.appendf("/%08X", files[n]);
1380 safeunlink(p2.c_str(), 0);
1382 }
1383
1384 debugs(36, 3, "Cleaned " << k << " unused files from " << p1);
1385 return k;
1386}
1387
AsHex< Integer > asHex(const Integer n)
a helper to ease AsHex object creation
Definition IoManip.h:169
int size
Definition ModDevPoll.cc:70
int GetInteger(void)
Definition Parsing.cc:148
time_t squid_curtime
RemovalPolicy * createRemovalPolicy(RemovalPolicySettings *settings)
Definition store.cc:1671
#define SQUIDSBUFPH
Definition SBuf.h:31
#define SQUIDSBUFPRINT(s)
Definition SBuf.h:32
class SquidConfig Config
#define INDEXSD(i)
Definition SquidConfig.h:74
StatCounters statCounter
static int rev_int_sort(const void *A, const void *B)
#define CLEAN_BUF_SZ
Definition UFSSwapDir.cc:11
static void FreeObject(void *address)
#define assert(EX)
Definition assert.h:17
std::mt19937::result_type RandomSeed32()
Definition Random.cc:13
void self_destruct(void)
Definition cache_cf.cc:275
static DiskIOModule * Find(char const *type)
virtual char const * type() const =0
virtual int load()
virtual bool shedLoad()
void openLog() override
bool pathIsDirectory(const char *path) const
void sync() override
prepare for shutdown
static EVH CleanEvent
Definition UFSSwapDir.h:130
void finalizeSwapoutFailure(StoreEntry &) override
abort the failed swapout that has been already noticed by Store
bool unlinkdUseful() const override
whether SwapDir may benefit from unlinkd
int callback() override
called once every main loop iteration; TODO: Move to UFS code.
void replacementAdd(StoreEntry *e)
void evictIfFound(const cache_key *) override
UFSSwapDir(char const *aType, const char *aModuleType)
StoreEntry * addDiskRestore(const cache_key *key, sfileno file_number, uint64_t swap_file_sz, time_t expires, time_t timestamp, time_t lastref, time_t lastmod, uint32_t refcount, uint16_t flags, int clean)
void mapBitReset(sfileno filn)
void optionIODump(StoreEntry *e) const
int createDirectory(const char *path, int)
void changeIO(DiskIOModule *)
~UFSSwapDir() override
void reconfigure() override
void finalizeSwapoutSuccess(const StoreEntry &) override
finalize the successful swapout that has been already noticed by Store
void parse(int index, char *path) override
void statfs(StoreEntry &) const override
static int DirClean(int swap_index)
SBuf logFile(char const *ext=nullptr) const
FILE * openTmpSwapLog(int *clean_flag, int *zero_flag)
void create() override
create system resources needed for this store to operate in the future
void mapBitSet(sfileno filn)
void evictCached(StoreEntry &) override
void reference(StoreEntry &) override
somebody needs this entry (many cache replacement policies need to know)
char * swapSubDir(int subdirn) const
static int * UFSDirToGlobalDirMapping
Definition UFSSwapDir.h:127
static size_t NumberOfUFSDirs
Definition UFSSwapDir.h:126
void init() override
int validFileno(sfileno filn, int flag) const
void replacementRemove(StoreEntry *e)
int mapBitTest(sfileno filn)
Fs::Ufs::UFSStrategy * IO
Definition UFSSwapDir.h:83
void dumpEntry(StoreEntry &) const
bool dereference(StoreEntry &) override
bool doubleCheck(StoreEntry &) override
void maintain() override
perform regular periodic maintenance; TODO: move to UFSSwapDir::Maintain
static bool IsUFSDir(SwapDir *sd)
ConfigOption * getOptionTree() const override
StoreIOState::Pointer createStoreIO(StoreEntry &, StoreIOState::STIOCB *, void *) override
StoreIOState::Pointer openStoreIO(StoreEntry &, StoreIOState::STIOCB *, void *) override
void logEntry(const StoreEntry &e, int op) const override
void dump(StoreEntry &) const override
static bool FilenoBelongsHere(int fn, int cachedir, int level1dir, int level2dir)
char * fullPath(sfileno, char *) const
int writeCleanStart() override
void closeLog() override
static int HandleCleanEvent()
safely cleans a few unused files if possible
void unlinkFile(sfileno f)
bool validL1(int) const
bool optionIOParse(char const *option, const char *value, int reconfiguring)
bool canStore(const StoreEntry &e, int64_t diskSpaceNeeded, int &load) const override
check whether we can store the entry; if we can, report current load
bool validL2(int) const
void writeCleanDone() override
void append(const char *c, int sz) override
Definition MemBuf.cc:209
char * space()
returns buffer after data; does not check space existence
Definition MemBuf.h:57
FREE * freeFunc()
Definition MemBuf.cc:303
void init(mb_size_t szInit, mb_size_t szMax)
Definition MemBuf.cc:93
char * content()
start of the added data
Definition MemBuf.h:41
mb_size_t contentSize() const
available data size
Definition MemBuf.h:47
void appended(mb_size_t sz)
updates content size after external append
Definition MemBuf.cc:226
C * getRaw() const
Definition RefCount.h:89
const StoreEntry *(* Next)(RemovalPolicyWalker *walker)
void(* Done)(RemovalPolicyWalker *walker)
void(* Done)(RemovalPurgeWalker *walker)
StoreEntry *(* Next)(RemovalPurgeWalker *walker)
Definition SBuf.h:94
const char * c_str()
Definition SBuf.cc:516
SBuf & appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Definition SBuf.cc:229
int cmp(const SBuf &S, const size_type n) const
shorthand version for compare()
Definition SBuf.h:279
SBuf & append(const SBuf &S)
Definition SBuf.cc:185
struct SquidConfig::@75 Swap
struct SquidConfig::@82 Log
RemovalPolicySettings * replPolicy
Definition SquidConfig.h:99
Store::DiskConfig cacheSwap
int highWaterMark
Definition SquidConfig.h:85
struct StatCounters::@113 swap
void hashInsert(const cache_key *)
Definition store.cc:424
bool swappedOut() const
whether the entire entry is now on disk (possibly marked for deletion)
Definition Store.h:135
uint16_t flags
Definition Store.h:231
sdirno swap_dirn
Definition Store.h:237
int locked() const
Definition Store.h:145
void dump(int debug_lvl) const
Definition store.cc:1499
time_t expires
Definition Store.h:225
void lastModified(const time_t when)
Definition Store.h:175
void release(const bool shareable=false)
Definition store.cc:1146
void detachFromDisk()
Definition store.cc:1953
bool hasDisk(const sdirno dirn=-1, const sfileno filen=-1) const
Definition store.cc:1929
time_t timestamp
Definition Store.h:223
sfileno swap_filen
unique ID inside a cache_dir for swapped out entries; -1 for others
Definition Store.h:235
void attachToDisk(const sdirno, const sfileno, const swap_status_t)
Definition store.cc:1940
RemovalPolicyNode repl
Definition Store.h:221
ping_status_t ping_status
Definition Store.h:241
store_status_t store_status
Definition Store.h:243
time_t lastref
Definition Store.h:224
uint64_t swap_file_sz
Definition Store.h:229
uint16_t refcount
Definition Store.h:230
void setMemStatus(mem_status_t)
Definition store.cc:1524
void STIOCB(void *their_data, int errflag, StoreIOState::Pointer self)
unsigned char key[SQUID_MD5_DIGEST_LENGTH]
void finalize()
call this before storing the log entry
size_t gapSize() const
number of bytes after the log header before the first log entry
static int store_dirs_rebuilding
the number of cache_dirs being rebuilt; TODO: move to Disks::Rebuilding
Definition Controller.h:133
manages a single cache_dir
Definition Disk.h:22
char * path
Definition Disk.h:102
virtual bool canStore(const StoreEntry &e, int64_t diskSpaceNeeded, int &load) const =0
check whether we can store the entry; if we can, report current load
Definition Disk.cc:164
CleanLog * cleanLog
Definition Disk.h:140
virtual ConfigOption * getOptionTree() const
Definition Disk.cc:258
char * outbuf
Definition UFSSwapDir.cc:58
off_t outbuf_offset
Definition UFSSwapDir.cc:59
SwapDir * sd
Definition UFSSwapDir.cc:62
UFSCleanLog(SwapDir *aSwapDir)
Definition UFSSwapDir.cc:47
void write(StoreEntry const &) override
"write" an entry to the clean log file.
Definition UFSSwapDir.cc:77
const StoreEntry * nextEntry() override
Get the next entry that is a candidate for clean log writing.
Definition UFSSwapDir.cc:66
RemovalPolicyWalker * walker
Definition UFSSwapDir.cc:61
#define MYNAME
Definition Stream.h:219
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
#define EBIT_CLR(flag, bit)
Definition defines.h:66
#define O_BINARY
Definition defines.h:134
@ ENTRY_VALIDATED
Definition enums.h:108
@ NOT_IN_MEMORY
Definition enums.h:30
@ PING_NONE
Has not considered whether to send ICP queries to peers yet.
Definition enums.h:36
@ SWAPOUT_DONE
Definition enums.h:59
@ STORE_OK
Definition enums.h:45
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition event.cc:107
void fatal(const char *message)
Definition fatal.cc:28
void fatalf(const char *fmt,...)
Definition fatal.cc:68
int FD_WRITE_METHOD(int fd, const char *buf, int len)
Definition fde.h:200
int file_open(const char *path, int mode)
Definition fs_io.cc:66
bool FileRename(const SBuf &from, const SBuf &to)
Definition fs_io.cc:443
void safeunlink(const char *s, int quiet)
Definition fs_io.cc:432
int fsBlockSize(const char *path, int *blksize)
Definition fs_io.cc:466
void file_close(int fd)
Definition fs_io.cc:92
int fsStats(const char *path, int *totl_kb, int *free_kb, int *totl_in, int *free_in)
Definition fs_io.cc:490
void file_write(int fd, off_t file_offset, void const *ptr_to_buf, int len, DWCB *handle, void *handle_data, FREE *free_func)
Definition fs_io.cc:313
int n_disk_objects
int reconfiguring
#define F1(x, y, z)
Definition md5.c:166
#define F2(x, y, z)
Definition md5.c:167
#define SQUID_MD5_DIGEST_LENGTH
Definition md5.h:66
int intPercent(const int a, const int b)
Definition SquidMath.cc:13
double doublePercent(const double, const double)
Definition SquidMath.cc:25
#define xfree
#define xstrdup
#define LOCAL_ARRAY(type, name, size)
Definition squid.h:62
#define MAXPATHLEN
Definition stdio.h:62
unsigned char cache_key
Store key.
Definition forward.h:29
signed_int32_t sfileno
Definition forward.h:22
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition store.cc:855
const char * storeKeyText(const cache_key *key)
void EVH void double
Definition stub_event.cc:16
int unsigned int
Definition stub_fd.cc:19
time_t getCurrentTime() STUB_RETVAL(0) int tvSubUsec(struct timeval
bool IamWorkerProcess()
whether the current process handles HTTP transactions and such
Definition stub_tools.cc:47
@ SWAP_LOG_ADD
Definition swap_log_op.h:14
#define PRIu64
Definition types.h:114
void * xcalloc(size_t n, size_t sz)
Definition xalloc.cc:71
#define safe_free(x)
Definition xalloc.h:73
const char * xstrerr(int error)
Definition xstrerror.cc:83
char * xstrncpy(char *dst, const char *src, size_t n)
Definition xstring.cc:37