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