11#define CLEAN_BUF_SZ 16384
137 const int *i1 = (
const int *)A;
138 const int *i2 = (
const int *)B;
147 fatal(
"UFSSwapDir::parseSizeL1L2: invalid size value");
149 const uint64_t
size =
static_cast<uint64_t
>(i) << 20;
153 if (
size == maxSize())
154 debugs(3, 2,
"Cache dir '" << path <<
"' size remains unchanged at " << i <<
" MB");
164 fatal(
"UFSSwapDir::parseSizeL1L2: invalid level 1 directories value");
169 fatal(
"UFSSwapDir::parseSizeL1L2: invalid level 2 directories value");
204 if (currentIOOptions && currentIOOptions->options.size() > 2) {
205 delete currentIOOptions->options.back();
206 currentIOOptions->options.pop_back();
212 if (currentIOOptions && ioOptions)
213 currentIOOptions->options.push_back(ioOptions);
219 if (strcmp(option,
"IOEngine") != 0)
254 if (currentIOOptions ==
nullptr)
257 currentIOOptions->options.push_back(parentResult);
262 currentIOOptions->options.push_back(ioOptions);
266 currentIOOptions =
nullptr;
274 debugs(47, 3,
"Initialising UFS SwapDir engine.");
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.";
284 if (verifyCacheDirs())
291 if (!started_clean_event) {
292 eventAdd(
"UFS storeDirClean", CleanEvent,
nullptr, 15.0, 1);
293 started_clean_event = 1;
302 debugs(47, 3,
"Creating swap space in " << path);
303 createDirectory(path, 0);
330 if (swaplog_fd > -1) {
337 delete currentIOOptions;
344 ", PATH " << fullPath(e.
swap_filen,
nullptr));
354 if (::stat(fullPath(e.
swap_filen,
nullptr), &sb) < 0) {
385 map->numFilesInMap(), map->capacity(),
387 x =
fsStats(path, &totl_kb, &free_kb, &totl_in, &free_in);
445 const uint64_t lowWaterSz = minSize();
447 if (currentSize() < lowWaterSz) {
448 debugs(47, 5,
"space still available in " << path);
456 static int64_t lastWarn = 0;
474 if (highWaterSz > lowWaterSz) {
476 f = (
double) (currentSize() - lowWaterSz) / (highWaterSz - lowWaterSz);
480 int max_scan = (
int) (f * 400.0 + 100.0);
483 int max_remove = (
int) (f * 300.0 + 20.0);
488 debugs(47, 3,
"f=" << f <<
", max_scan=" << max_scan <<
", max_remove=" << max_remove);
494 while (currentSize() >= lowWaterSz) {
499 if (removed >= max_remove)
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);
526 debugs(47, 3,
"referencing " << &e <<
" " <<
529 if (repl->Referenced)
530 repl->Referenced(repl, &e, &e.
repl);
536 debugs(47, 3,
"dereferencing " << &e <<
" " <<
539 if (repl->Dereferenced)
540 repl->Dereferenced(repl, &e, &e.
repl);
548 return IO->create(
this, &e, aCallback, callback_data);
554 return IO->open(
this, &e, aCallback, callback_data);
560 return map->testBit(filn);
580 if (map->testBit(filn))
588 fn = map->allocate(suggest);
598 assert(0 <= subdirn && subdirn < l1);
599 snprintf(fullfilename,
MAXPATHLEN,
"%s/%02X", path, subdirn);
611 if (0 == ::stat(aPath, &st)) {
612 if (S_ISDIR(st.st_mode)) {
615 fatalf(
"Swap directory %s is not a directory.", aPath);
617 }
else if (0 == mkdir(aPath, 0750)) {
622 fatalf(
"Failed to make swap directory %s: %s", aPath,
xstrerr(xerrno));
634 if (::stat(aPath, &sb) < 0) {
640 if (S_ISDIR(sb.st_mode) == 0) {
651 if (!pathIsDirectory(path))
654 for (
int j = 0; j < l1; ++j) {
655 char const *aPath = swapSubDir(j);
657 if (!pathIsDirectory(aPath))
669 for (
int i = 0; i < l1; ++i) {
670 snprintf(name,
MAXPATHLEN,
"%s/%02X", path, i);
674 if (createDirectory(name, 0))
681 for (
int k = 0; k < l2; ++k) {
682 snprintf(name,
MAXPATHLEN,
"%s/%02X/%02X", path, i, k);
683 createDirectory(name, should_exist);
698 while ((pathtmp2 = strchr(pathtmp2,
'/')))
702 int pos = strlen(pathtmp);
703 while (pos && pathtmp[pos-1] ==
'.')
704 pathtmp[--pos] =
'\0';
707 for (pathtmp2 = pathtmp; *pathtmp2 ==
'.'; ++pathtmp2);
719 lpath.
append(
"/swap.state", 11);
733 assert(NumberOfUFSDirs || !UFSDirToGlobalDirMapping);
742 SBuf logPath(logFile());
745 if (swaplog_fd < 0) {
748 fatal(
"UFSSwapDir::openLog: Failed to open swap log.");
751 debugs(50, 3,
"Cache Dir #" << index <<
" log opened on FD " << swaplog_fd);
760 assert(NumberOfUFSDirs > 0);
762 if (!NumberOfUFSDirs)
770 debugs(47, 3,
"Cache Dir #" << index <<
" log closed on FD " << swaplog_fd);
790 uint64_t swap_file_sz,
836 SBuf swaplog_path(logFile());
837 SBuf tmp_path(logFile(
".new"));
854 debugs(47, 3,
"Cache Dir #" << index <<
" log opened on FD " << fd);
862 SBuf swaplog_path(logFile());
863 SBuf clean_path(logFile(
".last-clean"));
864 SBuf new_path(logFile(
".new"));
868 struct stat clean_sb;
870 if (::stat(swaplog_path.
c_str(), &log_sb) < 0) {
875 *zero_flag = log_sb.st_size == 0 ? 1 : 0;
896 buf.
append(
reinterpret_cast<const char*
>(&header),
sizeof(header));
905 FILE *fp = fopen(swaplog_path.
c_str(),
"rb");
912 memset(&clean_sb,
'\0',
sizeof(
struct stat));
914 if (::stat(clean_path.
c_str(), &clean_sb) < 0)
916 else if (clean_sb.st_mtime < log_sb.st_mtime)
942 state->
cur = logFile();
943 state->
newLog = logFile(
".clean");
961 state->
walker = repl->WalkInit(repl);
963 debugs(47, 3,
"opened " << state->
newLog <<
", FD " << state->
fd);
966 if (::stat(state->
cur.
c_str(), &sb) == 0)
967 fchmod(state->
fd, sb.st_mode);
981 if (
nullptr == state)
1008 if (state->
fd >= 0) {
1009#if _SQUID_OS2_ || _SQUID_WINDOWS_
1041 static int swap_index = 0;
1045 if (!NumberOfUFSDirs)
1048 if (
nullptr == UFSDirToGlobalDirMapping) {
1054 UFSDirToGlobalDirMapping = (
int *)
xcalloc(NumberOfUFSDirs,
sizeof(*UFSDirToGlobalDirMapping));
1067 UFSDirToGlobalDirMapping[n] = i;
1070 j += (usd->
l1 * usd->
l2);
1073 assert(n == NumberOfUFSDirs);
1080 std::uniform_int_distribution<> dist(0, j);
1081 swap_index = dist(mt);
1086 n = DirClean(swap_index);
1096 const int n = HandleCleanEvent();
1097 eventAdd(
"storeDirClean", CleanEvent,
nullptr,
1098 15.0 * exp(-0.25 * n), 1);
1105 return (mySD !=
nullptr) ;
1131 D1 = ((filn / L2) / L2) % L1;
1136 D2 = (filn / L2) % L2;
1155 if (filn > map->capacity())
1164 debugs(79, 3,
"unlinking fileno " <<
asHex(f).upperCase().minDigits(8) <<
" '" <<
1165 fullPath(f,
nullptr) <<
"'");
1167 IO->unlinkFile(fullPath(f,
nullptr));
1193 replacementRemove(&e);
1209 debugs(47, 4,
"added node " << e <<
" to dir " << index);
1210 repl->Add(repl, e, &e->
repl);
1222 debugs(47, 4,
"remove node " << e <<
" from dir " << index);
1224 repl->Remove(repl, e, &e->
repl);
1231 dumpOptions(&entry);
1242 fullpath = fullfilename;
1246 snprintf(fullpath,
MAXPATHLEN,
"%s/%02X/%02X/%08X",
1248 ((filn / L2) / L2) % L1,
1258 return IO->callback();
1285 if (swaplog_fd < 0) {
1286 debugs(36, 5,
"cannot log " << e <<
" in the middle of reconfiguration");
1314 DIR *dir_pointer =
nullptr;
1323 N0 = NumberOfUFSDirs;
1324 D0 = UFSDirToGlobalDirMapping[swap_index % N0];
1328 D1 = (swap_index / N0) % N1;
1330 D2 = ((swap_index / N0) / N1) % N2;
1334 debugs(36, 3,
"Cleaning directory " << p1);
1335 dir_pointer = opendir(p1.
c_str());
1339 if (xerrno == ENOENT) {
1341 if (mkdir(p1.
c_str(), 0750) == 0)
1351 while ((de = readdir(dir_pointer)) !=
nullptr && k < 20) {
1352 if (sscanf(de->d_name,
"%X", &swapfileno) != 1)
1362 files[k] = swapfileno;
1366 closedir(dir_pointer);
1376 for (n = 0; n < k; ++n) {
1377 debugs(36, 3,
"Cleaning file " <<
asHex(files[n]).upperCase().minDigits(8));
1379 p2.
appendf(
"/%08X", files[n]);
1384 debugs(36, 3,
"Cleaned " << k <<
" unused files from " << p1);
AsHex< Integer > asHex(const Integer n)
a helper to ease AsHex object creation
RemovalPolicy * createRemovalPolicy(RemovalPolicySettings *settings)
#define SQUIDSBUFPRINT(s)
static int rev_int_sort(const void *A, const void *B)
static void FreeObject(void *address)
std::mt19937::result_type RandomSeed32()
static DiskIOModule * Find(char const *type)
virtual char const * type() const =0
bool pathIsDirectory(const char *path) const
void sync() override
prepare for shutdown
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 *)
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
static size_t NumberOfUFSDirs
int validFileno(sfileno filn, int flag) const
void replacementRemove(StoreEntry *e)
int mapBitTest(sfileno filn)
Fs::Ufs::UFSStrategy * IO
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
static int HandleCleanEvent()
safely cleans a few unused files if possible
void unlinkFile(sfileno f)
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
void writeCleanDone() override
void append(const char *c, int sz) override
char * space()
returns buffer after data; does not check space existence
void init(mb_size_t szInit, mb_size_t szMax)
char * content()
start of the added data
mb_size_t contentSize() const
available data size
void appended(mb_size_t sz)
updates content size after external append
const StoreEntry *(* Next)(RemovalPolicyWalker *walker)
void(* Done)(RemovalPolicyWalker *walker)
void(* Done)(RemovalPurgeWalker *walker)
StoreEntry *(* Next)(RemovalPurgeWalker *walker)
SBuf & appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
int cmp(const SBuf &S, const size_type n) const
shorthand version for compare()
SBuf & append(const SBuf &S)
struct SquidConfig::@75 Swap
struct SquidConfig::@82 Log
RemovalPolicySettings * replPolicy
Store::DiskConfig cacheSwap
struct StatCounters::@113 swap
void hashInsert(const cache_key *)
bool swappedOut() const
whether the entire entry is now on disk (possibly marked for deletion)
void dump(int debug_lvl) const
void lastModified(const time_t when)
void release(const bool shareable=false)
bool hasDisk(const sdirno dirn=-1, const sfileno filen=-1) const
sfileno swap_filen
unique ID inside a cache_dir for swapped out entries; -1 for others
void attachToDisk(const sdirno, const sfileno, const swap_status_t)
ping_status_t ping_status
store_status_t store_status
void setMemStatus(mem_status_t)
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
static int store_dirs_rebuilding
the number of cache_dirs being rebuilt; TODO: move to Disks::Rebuilding
manages a single cache_dir
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
virtual ConfigOption * getOptionTree() const
UFSCleanLog(SwapDir *aSwapDir)
void write(StoreEntry const &) override
"write" an entry to the clean log file.
const StoreEntry * nextEntry() override
Get the next entry that is a candidate for clean log writing.
RemovalPolicyWalker * walker
#define debugs(SECTION, LEVEL, CONTENT)
#define EBIT_CLR(flag, bit)
@ PING_NONE
Has not considered whether to send ICP queries to peers yet.
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
void fatal(const char *message)
void fatalf(const char *fmt,...)
int FD_WRITE_METHOD(int fd, const char *buf, int len)
int file_open(const char *path, int mode)
bool FileRename(const SBuf &from, const SBuf &to)
void safeunlink(const char *s, int quiet)
int fsBlockSize(const char *path, int *blksize)
int fsStats(const char *path, int *totl_kb, int *free_kb, int *totl_in, int *free_in)
void file_write(int fd, off_t file_offset, void const *ptr_to_buf, int len, DWCB *handle, void *handle_data, FREE *free_func)
#define SQUID_MD5_DIGEST_LENGTH
int intPercent(const int a, const int b)
double doublePercent(const double, const double)
#define LOCAL_ARRAY(type, name, size)
unsigned char cache_key
Store key.
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
const char * storeKeyText(const cache_key *key)
time_t getCurrentTime() STUB_RETVAL(0) int tvSubUsec(struct timeval
void * xcalloc(size_t n, size_t sz)
const char * xstrerr(int error)
char * xstrncpy(char *dst, const char *src, size_t n)