2 * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 47 Store Directory Routines */
14 #include "RebuildState.h"
15 #include "SquidConfig.h"
16 #include "SquidTime.h"
17 #include "store/Disks.h"
18 #include "store_key_md5.h"
19 #include "store_rebuild.h"
20 #include "StoreSwapLogData.h"
22 #include "UFSSwapLogParser.h"
30 CBDATA_NAMESPACED_CLASS_INIT(Fs::Ufs
,RebuildState
);
32 Fs::Ufs::RebuildState::RebuildState(RefCount
<UFSSwapDir
> aSwapDir
) :
51 * If the swap.state file exists in the cache_dir, then
52 * we'll use commonUfsDirRebuildFromSwapLog(), otherwise we'll
53 * use commonUfsDirRebuildFromDirectory() to open up each file
54 * and suck in the meta data.
56 int clean
= 0; //TODO: change to bool
57 int zeroLengthLog
= 0;
58 FILE *fp
= sd
->openTmpSwapLog(&clean
, &zeroLengthLog
);
60 if (fp
&& !zeroLengthLog
)
61 LogParser
= Fs::Ufs::UFSSwapLogParser::GetUFSSwapLogParser(fp
);
63 if (LogParser
== NULL
) {
71 flags
.clean
= (clean
!= 0);
75 flags
.need_to_validate
= true;
77 debugs(47, DBG_IMPORTANT
, "Rebuilding storage in " << sd
->path
<< " (" <<
78 (clean
? "clean log" : (LogParser
? "dirty log" : "no log")) << ")");
81 Fs::Ufs::RebuildState::~RebuildState()
83 sd
->closeTmpSwapLog();
90 Fs::Ufs::RebuildState::RebuildStep(void *data
)
92 RebuildState
*rb
= (RebuildState
*)data
;
96 // delay storeRebuildComplete() when reconfiguring to protect storeCleanup()
97 if (!rb
->isDone() || reconfiguring
)
98 eventAdd("storeRebuild", RebuildStep
, rb
, 0.01, 1);
100 -- StoreController::store_dirs_rebuilding
;
101 storeRebuildComplete(&rb
->counts
);
106 /// load entries from swap.state or files until we run out of entries or time
108 Fs::Ufs::RebuildState::rebuildStep()
110 // Balance our desire to maximize the number of entries processed at once
111 // (and, hence, minimize overheads and total rebuild time) with a
112 // requirement to also process Coordinator events, disk I/Os, etc.
113 const int maxSpentMsec
= 50; // keep small: most RAM I/Os are under 1ms
114 const timeval loopStart
= current_time
;
116 const int totalEntries
= LogParser
? LogParser
->SwapLogEntries() : -1;
120 rebuildFromSwapLog();
122 rebuildFromDirectory();
124 // TODO: teach storeRebuildProgress to handle totalEntries <= 0
125 if (totalEntries
> 0 && (n_read
% 4000 == 0))
126 storeRebuildProgress(sd
->index
, totalEntries
, n_read
);
128 if (opt_foreground_rebuild
)
129 continue; // skip "few entries at a time" check below
132 const double elapsedMsec
= tvSubMsec(loopStart
, current_time
);
133 if (elapsedMsec
> maxSpentMsec
|| elapsedMsec
< 0) {
134 debugs(47, 5, HERE
<< "pausing after " << n_read
<< " entries in " <<
135 elapsedMsec
<< "ms; " << (elapsedMsec
/n_read
) << "ms per entry");
141 /// process one cache file
143 Fs::Ufs::RebuildState::rebuildFromDirectory()
145 cache_key key
[SQUID_MD5_DIGEST_LENGTH
];
149 debugs(47, 3, HERE
<< "DIR #" << sd
->index
);
154 fd
= getNextFile(&filn
, &size
);
157 debugs(47, DBG_IMPORTANT
, "Done scanning " << sd
->path
<< " dir (" <<
158 n_read
<< " entries)");
166 /* lets get file stats here */
170 if (fstat(fd
, &sb
) < 0) {
172 debugs(47, DBG_IMPORTANT
, MYNAME
<< "fstat(FD " << fd
<< "): " << xstrerr(xerrno
));
174 --store_open_disk_fd
;
180 buf
.init(SM_PAGE_SIZE
, SM_PAGE_SIZE
);
181 if (!storeRebuildLoadEntry(fd
, sd
->index
, buf
, counts
))
184 const uint64_t expectedSize
= sb
.st_size
> 0 ?
185 static_cast<uint64_t>(sb
.st_size
) : 0;
188 const bool parsed
= storeRebuildParseEntry(buf
, tmpe
, key
, counts
,
192 --store_open_disk_fd
;
195 bool accepted
= parsed
&& tmpe
.swap_file_sz
> 0;
196 if (parsed
&& !accepted
) {
197 debugs(47, DBG_IMPORTANT
, "WARNING: Ignoring ufs cache entry with " <<
198 "unknown size: " << tmpe
);
203 // XXX: shouldn't this be a call to commonUfsUnlink?
204 sd
->unlinkFile(filn
); // should we unlink in all failure cases?
219 /// if the loaded entry metadata is still relevant, indexes the entry
221 Fs::Ufs::RebuildState::addIfFresh(const cache_key
*key
,
223 uint64_t swap_file_sz
,
231 if (!evictStaleAndContinue(key
, lastref
, counts
.dupcount
))
235 const auto addedEntry
= sd
->addDiskRestore(key
,
244 0 /* XXX: unused */);
245 storeDirSwapLog(addedEntry
, SWAP_LOG_ADD
);
248 /// Evicts a matching entry if it was last touched before caller's maxRef.
249 /// \returns false only if the matching entry was touched at or after maxRef,
250 /// indicating that the caller has supplied outdated maxRef.
252 Fs::Ufs::RebuildState::evictStaleAndContinue(const cache_key
*candidateKey
, const time_t maxRef
, int &staleCount
)
254 if (auto *indexedEntry
= Store::Root().peek(candidateKey
)) {
256 if (indexedEntry
->lastref
>= maxRef
) {
257 indexedEntry
->abandon("Fs::Ufs::RebuildState::evictStaleAndContinue");
263 indexedEntry
->release(true); // evict previously indexedEntry
269 /// process one swap log entry
271 Fs::Ufs::RebuildState::rebuildFromSwapLog()
273 StoreSwapLogData swapData
;
275 if (LogParser
->ReadRecord(swapData
) != 1) {
276 debugs(47, DBG_IMPORTANT
, "Done reading " << sd
->path
<< " swaplog (" << n_read
<< " entries)");
286 if (!swapData
.sane()) {
292 * BC: during 2.4 development, we changed the way swap file
293 * numbers are assigned and stored. The high 16 bits used
294 * to encode the SD index number. There used to be a call
295 * to storeDirProperFileno here that re-assigned the index
296 * bits. Now, for backwards compatibility, we just need
299 swapData
.swap_filen
&= 0x00FFFFFF;
301 debugs(47, 3, HERE
<< swap_log_op_str
[(int) swapData
.op
] << " " <<
302 storeKeyText(swapData
.key
) << " "<< std::setfill('0') <<
303 std::hex
<< std::uppercase
<< std::setw(8) <<
304 swapData
.swap_filen
);
306 if (swapData
.op
== SWAP_LOG_ADD
) {
308 } else if (swapData
.op
== SWAP_LOG_DEL
) {
309 // remove any older or same-age entry; +1 covers same-age entries
310 (void)evictStaleAndContinue(swapData
.key
, swapData
.lastref
+1, counts
.cancelcount
);
314 x
= ::log(static_cast<double>(++counts
.bad_log_op
)) / ::log(10.0);
316 if (0.0 == x
- (double) (int) x
)
317 debugs(47, DBG_IMPORTANT
, "WARNING: " << counts
.bad_log_op
<< " invalid swap log entries found");
324 ++counts
.scancount
; // XXX: should not this be incremented earlier?
326 if (!sd
->validFileno(swapData
.swap_filen
, 0)) {
331 if (EBIT_TEST(swapData
.flags
, KEY_PRIVATE
)) {
336 if (sd
->mapBitTest(swapData
.swap_filen
)) {
337 // While we were scanning the logs, some (unrelated) entry was added to
338 // our disk using our logged swap_filen. We could change our swap_filen
339 // and move the store file, but there is no Store API to do that (TODO).
344 addIfFresh(swapData
.key
,
346 swapData
.swap_file_sz
,
356 Fs::Ufs::RebuildState::getNextFile(sfileno
* filn_p
, int *)
360 debugs(47, 3, HERE
<< "flag=" << flags
.init
<< ", " <<
361 sd
->index
<< ": /"<< std::setfill('0') << std::hex
<<
362 std::uppercase
<< std::setw(2) << curlvl1
<< "/" << std::setw(2) <<
368 while (fd
< 0 && done
== 0) {
371 if (!flags
.init
) { /* initialize, open first file */
372 // XXX: 0's should not be needed, constructor inits now
378 assert(Config
.cacheSwap
.n_configured
> 0);
381 if (0 == in_dir
) { /* we need to read in a new directory */
382 snprintf(fullpath
, sizeof(fullpath
), "%s/%02X/%02X",
389 td
= opendir(fullpath
);
395 debugs(47, DBG_IMPORTANT
, MYNAME
<< "error in opendir (" << fullpath
<< "): " << xstrerr(xerrno
));
397 entry
= readdir(td
); /* skip . and .. */
400 if (entry
== NULL
&& errno
== ENOENT
)
401 debugs(47, DBG_IMPORTANT
, HERE
<< "WARNING: directory does not exist!");
402 debugs(47, 3, HERE
<< "Directory " << fullpath
);
406 if (td
!= NULL
&& (entry
= readdir(td
)) != NULL
) {
409 if (sscanf(entry
->d_name
, "%x", &fn
) != 1) {
410 debugs(47, 3, HERE
<< "invalid entry " << entry
->d_name
);
414 if (!UFSSwapDir::FilenoBelongsHere(fn
, sd
->index
, curlvl1
, curlvl2
)) {
415 debugs(47, 3, HERE
<< std::setfill('0') <<
416 std::hex
<< std::uppercase
<< std::setw(8) << fn
<<
417 " does not belong in " << std::dec
<< sd
->index
<< "/" <<
418 curlvl1
<< "/" << curlvl2
);
423 if (sd
->mapBitTest(fn
)) {
424 debugs(47, 3, HERE
<< "Locked, continuing with next.");
428 snprintf(fullfilename
, sizeof(fullfilename
), "%s/%s",
429 fullpath
, entry
->d_name
);
430 debugs(47, 3, HERE
<< "Opening " << fullfilename
);
431 fd
= file_open(fullfilename
, O_RDONLY
| O_BINARY
);
435 debugs(47, DBG_IMPORTANT
, MYNAME
<< "error opening " << fullfilename
<< ": " << xstrerr(xerrno
));
437 ++store_open_disk_fd
;
449 if (sd
->validL2(++curlvl2
))
454 if (sd
->validL1(++curlvl1
))
467 Fs::Ufs::RebuildState::error() const
473 Fs::Ufs::RebuildState::isDone() const