]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ipc/StoreMap.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / ipc / StoreMap.cc
CommitLineData
44c95fcf 1/*
f70aedc4 2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
bbc27441
AJ
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.
44c95fcf
AR
7 */
8
bbc27441
AJ
9/* DEBUG: section 54 Interprocess Communication */
10
582c2af2 11#include "squid.h"
5bed43d6 12#include "ipc/StoreMap.h"
65e41a45 13#include "sbuf/SBuf.h"
b2aca62a
EB
14#include "SquidConfig.h"
15#include "StatCounters.h"
5bed43d6 16#include "Store.h"
4310f8b0 17#include "store/Controller.h"
602d9612 18#include "store_key_md5.h"
5bed43d6 19#include "tools.h"
44c95fcf 20
b2aca62a
EB
21#include <chrono>
22
1860fbac
AR
23static SBuf
24StoreMapSlicesId(const SBuf &path)
44c95fcf 25{
1860fbac 26 return Ipc::Mem::Segment::Name(path, "slices");
44c95fcf
AR
27}
28
1860fbac
AR
29static SBuf
30StoreMapAnchorsId(const SBuf &path)
44c95fcf 31{
1860fbac 32 return Ipc::Mem::Segment::Name(path, "anchors");
44c95fcf
AR
33}
34
abf396ec
AR
35static SBuf
36StoreMapFileNosId(const SBuf &path)
37{
38 return Ipc::Mem::Segment::Name(path, "filenos");
39}
40
1860fbac
AR
41Ipc::StoreMap::Owner *
42Ipc::StoreMap::Init(const SBuf &path, const int sliceLimit)
43{
44 assert(sliceLimit > 0); // we should not be created otherwise
45 const int anchorLimit = min(sliceLimit, static_cast<int>(SwapFilenMax));
46 Owner *owner = new Owner;
abf396ec 47 owner->fileNos = shm_new(FileNos)(StoreMapFileNosId(path).c_str(), anchorLimit);
1860fbac
AR
48 owner->anchors = shm_new(Anchors)(StoreMapAnchorsId(path).c_str(), anchorLimit);
49 owner->slices = shm_new(Slices)(StoreMapSlicesId(path).c_str(), sliceLimit);
50 debugs(54, 5, "created " << path << " with " << anchorLimit << '+' << sliceLimit);
51 return owner;
52}
53
54Ipc::StoreMap::StoreMap(const SBuf &aPath): cleaner(NULL), path(aPath),
abf396ec 55 fileNos(shm_old(FileNos)(StoreMapFileNosId(path).c_str())),
f53969cc 56 anchors(shm_old(Anchors)(StoreMapAnchorsId(path).c_str())),
b2aca62a
EB
57 slices(shm_old(Slices)(StoreMapSlicesId(path).c_str())),
58 hitValidation(true)
c011f9bc 59{
1860fbac 60 debugs(54, 5, "attached " << path << " with " <<
abf396ec 61 fileNos->capacity << '+' <<
1860fbac
AR
62 anchors->capacity << '+' << slices->capacity);
63 assert(entryLimit() > 0); // key-to-position mapping requires this
64 assert(entryLimit() <= sliceLimit()); // at least one slice per entry
c011f9bc
DK
65}
66
50dc81ec
AR
67int
68Ipc::StoreMap::compareVersions(const sfileno fileno, time_t newVersion) const
69{
1860fbac 70 const Anchor &inode = anchorAt(fileno);
50dc81ec 71
2f8abb64 72 // note: we do not lock, so comparison may be inaccurate
50dc81ec 73
ce49546e 74 if (inode.empty())
50dc81ec
AR
75 return +2;
76
77 if (const time_t diff = newVersion - inode.basics.timestamp)
78 return diff < 0 ? -1 : +1;
9d4e9cfb 79
50dc81ec
AR
80 return 0;
81}
82
83void
84Ipc::StoreMap::forgetWritingEntry(sfileno fileno)
85{
1860fbac 86 Anchor &inode = anchorAt(fileno);
50dc81ec 87
ce49546e 88 assert(inode.writing());
50dc81ec
AR
89
90 // we do not iterate slices because we were told to forget about
91 // them; the caller is responsible for freeing them (most likely
92 // our slice list is incomplete or has holes)
93
ce49546e 94 inode.rewind();
50dc81ec
AR
95
96 inode.lock.unlockExclusive();
51dcaa03 97 --anchors->count;
50dc81ec
AR
98
99 debugs(54, 8, "closed entry " << fileno << " for writing " << path);
100}
101
5bd8ce13
AR
102const Ipc::StoreMap::Anchor *
103Ipc::StoreMap::openOrCreateForReading(const cache_key *const key, sfileno &fileno, const StoreEntry &entry)
104{
105 debugs(54, 5, "opening/creating entry with key " << storeKeyText(key)
106 << " for reading " << path);
107
108 // start with reading so that we do not overwrite an existing unlocked entry
109 auto idx = fileNoByKey(key);
110 if (const auto anchor = openForReadingAt(idx, key)) {
111 fileno = idx;
112 return anchor;
113 }
114
115 // the competing openOrCreateForReading() workers race to create a new entry
116 idx = fileNoByKey(key);
117 if (auto anchor = openForWritingAt(idx)) {
118 anchor->set(entry, key);
119 anchor->lock.switchExclusiveToShared();
120 // race ended
121 assert(anchor->complete());
122 fileno = idx;
123 debugs(54, 5, "switched entry " << fileno << " from writing to reading " << path);
124 return anchor;
125 }
126
127 // we lost the above race; see if the winner-created entry is now readable
128 // TODO: Do some useful housekeeping work here to give the winner more time.
129 idx = fileNoByKey(key);
130 if (const auto anchor = openForReadingAt(idx, key)) {
131 fileno = idx;
132 return anchor;
133 }
134
135 // slow entry creator or some other problem
136 return nullptr;
137}
138
50dc81ec 139Ipc::StoreMap::Anchor *
44c95fcf
AR
140Ipc::StoreMap::openForWriting(const cache_key *const key, sfileno &fileno)
141{
50dc81ec 142 debugs(54, 5, "opening entry with key " << storeKeyText(key)
6d68a230 143 << " for writing " << path);
abf396ec 144 const int idx = fileNoByKey(key);
44c95fcf 145
50dc81ec
AR
146 if (Anchor *anchor = openForWritingAt(idx)) {
147 fileno = idx;
148 return anchor;
149 }
150
151 return NULL;
152}
153
154Ipc::StoreMap::Anchor *
155Ipc::StoreMap::openForWritingAt(const sfileno fileno, bool overwriteExisting)
156{
1860fbac 157 Anchor &s = anchorAt(fileno);
44c95fcf
AR
158 ReadWriteLock &lock = s.lock;
159
160 if (lock.lockExclusive()) {
ce49546e 161 assert(s.writing() && !s.reading());
50dc81ec
AR
162
163 // bail if we cannot empty this position
ce49546e 164 if (!s.waitingToBeFreed && !s.empty() && !overwriteExisting) {
50dc81ec 165 lock.unlockExclusive();
6d68a230
AR
166 debugs(54, 5, "cannot open existing entry " << fileno <<
167 " for writing " << path);
50dc81ec
AR
168 return NULL;
169 }
44c95fcf 170
7f6748c8 171 // free if the entry was used, keeping the entry locked
ce49546e 172 if (s.waitingToBeFreed || !s.empty())
50dc81ec 173 freeChain(fileno, s, true);
44c95fcf 174
ce49546e 175 assert(s.empty());
a3023c03 176 s.start = -1; // we have not allocated any slices yet
abf396ec 177 s.splicingPoint = -1;
1860fbac 178 ++anchors->count;
50dc81ec 179
44c95fcf 180 //s.setKey(key); // XXX: the caller should do that
50dc81ec 181 debugs(54, 5, "opened entry " << fileno << " for writing " << path);
44c95fcf 182 return &s; // and keep the entry locked
6d68a230 183 }
44c95fcf 184
6d68a230
AR
185 debugs(54, 5, "cannot open busy entry " << fileno <<
186 " for writing " << path);
44c95fcf
AR
187 return NULL;
188}
189
ce49546e
AR
190void
191Ipc::StoreMap::startAppending(const sfileno fileno)
192{
1860fbac 193 Anchor &s = anchorAt(fileno);
ce49546e
AR
194 assert(s.writing());
195 s.lock.startAppending();
196 debugs(54, 5, "restricted entry " << fileno << " to appending " << path);
197}
198
44c95fcf 199void
8253d451 200Ipc::StoreMap::closeForWriting(const sfileno fileno)
44c95fcf 201{
1860fbac 202 Anchor &s = anchorAt(fileno);
ce49546e 203 assert(s.writing());
8253d451
AR
204 // TODO: assert(!s.empty()); // i.e., unlocked s becomes s.complete()
205 s.lock.unlockExclusive();
206 debugs(54, 5, "closed entry " << fileno << " for writing " << path);
207 // cannot assert completeness here because we have no lock
208}
209
210void
211Ipc::StoreMap::switchWritingToReading(const sfileno fileno)
212{
213 debugs(54, 5, "switching entry " << fileno << " from writing to reading " << path);
214 Anchor &s = anchorAt(fileno);
215 assert(s.writing());
216 s.lock.switchExclusiveToShared();
217 assert(s.complete());
50dc81ec
AR
218}
219
220Ipc::StoreMap::Slice &
221Ipc::StoreMap::writeableSlice(const AnchorId anchorId, const SliceId sliceId)
222{
1860fbac 223 assert(anchorAt(anchorId).writing());
36c84e19 224 assert(validSlice(sliceId));
1860fbac 225 return sliceAt(sliceId);
50dc81ec
AR
226}
227
228const Ipc::StoreMap::Slice &
229Ipc::StoreMap::readableSlice(const AnchorId anchorId, const SliceId sliceId) const
230{
1860fbac 231 assert(anchorAt(anchorId).reading());
36c84e19 232 assert(validSlice(sliceId));
1860fbac 233 return sliceAt(sliceId);
50dc81ec
AR
234}
235
236Ipc::StoreMap::Anchor &
237Ipc::StoreMap::writeableEntry(const AnchorId anchorId)
238{
1860fbac
AR
239 assert(anchorAt(anchorId).writing());
240 return anchorAt(anchorId);
ce49546e
AR
241}
242
243const Ipc::StoreMap::Anchor &
244Ipc::StoreMap::readableEntry(const AnchorId anchorId) const
245{
1860fbac
AR
246 assert(anchorAt(anchorId).reading());
247 return anchorAt(anchorId);
44c95fcf
AR
248}
249
44c95fcf
AR
250void
251Ipc::StoreMap::abortWriting(const sfileno fileno)
252{
6d68a230 253 debugs(54, 5, "aborting entry " << fileno << " for writing " << path);
1860fbac 254 Anchor &s = anchorAt(fileno);
ce49546e
AR
255 assert(s.writing());
256 s.lock.appending = false; // locks out any new readers
257 if (!s.lock.readers) {
258 freeChain(fileno, s, false);
259 debugs(54, 5, "closed clean entry " << fileno << " for writing " << path);
260 } else {
261 s.waitingToBeFreed = true;
4310f8b0 262 s.writerHalted = true;
ce49546e
AR
263 s.lock.unlockExclusive();
264 debugs(54, 5, "closed dirty entry " << fileno << " for writing " << path);
9d4e9cfb 265 }
44c95fcf
AR
266}
267
abf396ec
AR
268void
269Ipc::StoreMap::abortUpdating(Update &update)
270{
271 const sfileno fileno = update.stale.fileNo;
272 debugs(54, 5, "aborting entry " << fileno << " for updating " << path);
273 if (update.stale) {
274 AssertFlagIsSet(update.stale.anchor->lock.updating);
275 update.stale.anchor->lock.unlockHeaders();
276 closeForReading(update.stale.fileNo);
277 update.stale = Update::Edition();
278 }
279 if (update.fresh) {
280 abortWriting(update.fresh.fileNo);
281 update.fresh = Update::Edition();
282 }
283 debugs(54, 5, "aborted entry " << fileno << " for updating " << path);
284}
285
50dc81ec 286const Ipc::StoreMap::Anchor *
44c95fcf
AR
287Ipc::StoreMap::peekAtReader(const sfileno fileno) const
288{
1860fbac 289 const Anchor &s = anchorAt(fileno);
ce49546e 290 if (s.reading())
44c95fcf 291 return &s; // immediate access by lock holder so no locking
d1d3b4dc
EB
292 assert(s.writing()); // must be locked for reading or writing
293 return nullptr;
294}
295
296const Ipc::StoreMap::Anchor *
297Ipc::StoreMap::peekAtWriter(const sfileno fileno) const
298{
299 const Anchor &s = anchorAt(fileno);
ce49546e 300 if (s.writing())
d1d3b4dc
EB
301 return &s; // immediate access by lock holder so no locking
302 assert(s.reading()); // must be locked for reading or writing
303 return nullptr;
44c95fcf
AR
304}
305
d366a7fa
AR
306const Ipc::StoreMap::Anchor &
307Ipc::StoreMap::peekAtEntry(const sfileno fileno) const
308{
1860fbac 309 return anchorAt(fileno);
d366a7fa
AR
310}
311
4310f8b0 312bool
50dc81ec 313Ipc::StoreMap::freeEntry(const sfileno fileno)
44c95fcf 314{
6d68a230 315 debugs(54, 5, "marking entry " << fileno << " to be freed in " << path);
44c95fcf 316
1860fbac 317 Anchor &s = anchorAt(fileno);
44c95fcf 318
4310f8b0
EB
319 if (s.lock.lockExclusive()) {
320 const bool result = !s.waitingToBeFreed && !s.empty();
50dc81ec 321 freeChain(fileno, s, false);
4310f8b0
EB
322 return result;
323 }
324
325 uint8_t expected = false;
326 // mark to free the locked entry later (if not already marked)
327 return s.waitingToBeFreed.compare_exchange_strong(expected, true);
44c95fcf
AR
328}
329
ce49546e
AR
330void
331Ipc::StoreMap::freeEntryByKey(const cache_key *const key)
332{
333 debugs(54, 5, "marking entry with key " << storeKeyText(key)
334 << " to be freed in " << path);
335
abf396ec 336 const int idx = fileNoByKey(key);
1860fbac 337 Anchor &s = anchorAt(idx);
ce49546e
AR
338 if (s.lock.lockExclusive()) {
339 if (s.sameKey(key))
340 freeChain(idx, s, true);
341 s.lock.unlockExclusive();
9d4e9cfb 342 } else if (s.lock.lockShared()) {
ce49546e
AR
343 if (s.sameKey(key))
344 s.waitingToBeFreed = true; // mark to free it later
345 s.lock.unlockShared();
346 } else {
9d4e9cfb
AR
347 // we cannot be sure that the entry we found is ours because we do not
348 // have a lock on it, but we still check to minimize false deletions
349 if (s.sameKey(key))
350 s.waitingToBeFreed = true; // mark to free it later
ce49546e
AR
351 }
352}
353
4310f8b0
EB
354bool
355Ipc::StoreMap::markedForDeletion(const cache_key *const key)
356{
357 const int idx = fileNoByKey(key);
358 const Anchor &s = anchorAt(idx);
359 return s.sameKey(key) ? bool(s.waitingToBeFreed) : false;
360}
361
362bool
363Ipc::StoreMap::hasReadableEntry(const cache_key *const key)
364{
365 sfileno index;
366 if (openForReading(reinterpret_cast<const cache_key*>(key), index)) {
367 closeForReading(index);
368 return true;
369 }
370 return false;
371}
372
50dc81ec
AR
373/// unconditionally frees an already locked chain of slots, unlocking if needed
374void
375Ipc::StoreMap::freeChain(const sfileno fileno, Anchor &inode, const bool keepLocked)
376{
ce49546e 377 debugs(54, 7, "freeing entry " << fileno <<
6d68a230 378 " in " << path);
abf396ec
AR
379 if (!inode.empty())
380 freeChainAt(inode.start, inode.splicingPoint);
ce49546e 381 inode.rewind();
50dc81ec
AR
382
383 if (!keepLocked)
384 inode.lock.unlockExclusive();
1860fbac 385 --anchors->count;
6d68a230 386 debugs(54, 5, "freed entry " << fileno << " in " << path);
50dc81ec
AR
387}
388
abf396ec
AR
389/// unconditionally frees an already locked chain of slots; no anchor maintenance
390void
391Ipc::StoreMap::freeChainAt(SliceId sliceId, const SliceId splicingPoint)
392{
393 static uint64_t ChainId = 0; // to pair freeing/freed calls in debugs()
394 const uint64_t chainId = ++ChainId;
395 debugs(54, 7, "freeing chain #" << chainId << " starting at " << sliceId << " in " << path);
396 while (sliceId >= 0) {
397 Slice &slice = sliceAt(sliceId);
398 const SliceId nextId = slice.next;
c2037547 399 slice.clear();
abf396ec
AR
400 if (cleaner)
401 cleaner->noteFreeMapSlice(sliceId); // might change slice state
402 if (sliceId == splicingPoint) {
403 debugs(54, 5, "preserving chain #" << chainId << " in " << path <<
404 " suffix after slice " << splicingPoint);
405 break; // do not free the rest of the chain
406 }
407 sliceId = nextId;
408 }
409 debugs(54, 7, "freed chain #" << chainId << " in " << path);
410}
411
c2037547
EB
412void
413Ipc::StoreMap::prepFreeSlice(const SliceId sliceId)
414{
415 // TODO: Move freeSlots here, along with reserveSlotForWriting() logic.
416 assert(validSlice(sliceId));
417 sliceAt(sliceId).clear();
418}
419
abf396ec
AR
420Ipc::StoreMap::SliceId
421Ipc::StoreMap::sliceContaining(const sfileno fileno, const uint64_t bytesNeeded) const
422{
423 const Anchor &anchor = anchorAt(fileno);
424 Must(anchor.reading());
425 uint64_t bytesSeen = 0;
426 SliceId lastSlice = anchor.start;
427 while (lastSlice >= 0) {
428 const Slice &slice = sliceAt(lastSlice);
429 bytesSeen += slice.size;
430 if (bytesSeen >= bytesNeeded)
431 break;
432 lastSlice = slice.next;
433 }
434 debugs(54, 7, "entry " << fileno << " has " << bytesNeeded << '/' << bytesSeen <<
435 " bytes at slice " << lastSlice << " in " << path);
436 return lastSlice; // may be negative
437}
438
50dc81ec 439const Ipc::StoreMap::Anchor *
44c95fcf
AR
440Ipc::StoreMap::openForReading(const cache_key *const key, sfileno &fileno)
441{
50dc81ec 442 debugs(54, 5, "opening entry with key " << storeKeyText(key)
6d68a230 443 << " for reading " << path);
abf396ec 444 const int idx = fileNoByKey(key);
b2aca62a
EB
445 if (const auto anchor = openForReadingAt(idx, key)) {
446 fileno = idx;
447 return anchor; // locked for reading
44c95fcf 448 }
44c95fcf
AR
449 return NULL;
450}
451
50dc81ec 452const Ipc::StoreMap::Anchor *
b2aca62a 453Ipc::StoreMap::openForReadingAt(const sfileno fileno, const cache_key *const key)
44c95fcf 454{
6d68a230 455 debugs(54, 5, "opening entry " << fileno << " for reading " << path);
1860fbac 456 Anchor &s = anchorAt(fileno);
44c95fcf
AR
457
458 if (!s.lock.lockShared()) {
6d68a230
AR
459 debugs(54, 5, "cannot open busy entry " << fileno <<
460 " for reading " << path);
44c95fcf
AR
461 return NULL;
462 }
463
ce49546e 464 if (s.empty()) {
44c95fcf 465 s.lock.unlockShared();
6d68a230
AR
466 debugs(54, 7, "cannot open empty entry " << fileno <<
467 " for reading " << path);
44c95fcf
AR
468 return NULL;
469 }
470
471 if (s.waitingToBeFreed) {
472 s.lock.unlockShared();
539283df 473 debugs(54, 7, "cannot open marked entry " << fileno <<
6d68a230 474 " for reading " << path);
44c95fcf
AR
475 return NULL;
476 }
477
b2aca62a
EB
478 if (!s.sameKey(key)) {
479 s.lock.unlockShared();
480 debugs(54, 5, "cannot open wrong-key entry " << fileno <<
481 " for reading " << path);
482 return nullptr;
483 }
484
485 if (Config.paranoid_hit_validation.count() && hitValidation && !validateHit(fileno)) {
486 s.lock.unlockShared();
487 debugs(54, 5, "cannot open corrupted entry " << fileno <<
488 " for reading " << path);
489 return nullptr;
490 }
491
50dc81ec 492 debugs(54, 5, "opened entry " << fileno << " for reading " << path);
44c95fcf
AR
493 return &s;
494}
495
496void
497Ipc::StoreMap::closeForReading(const sfileno fileno)
498{
1860fbac 499 Anchor &s = anchorAt(fileno);
ce49546e 500 assert(s.reading());
44c95fcf 501 s.lock.unlockShared();
50dc81ec
AR
502 debugs(54, 5, "closed entry " << fileno << " for reading " << path);
503}
504
d1d3b4dc
EB
505void
506Ipc::StoreMap::closeForReadingAndFreeIdle(const sfileno fileno)
507{
508 auto &s = anchorAt(fileno);
509 assert(s.reading());
510
511 if (!s.lock.unlockSharedAndSwitchToExclusive()) {
512 debugs(54, 5, "closed entry " << fileno << " for reading " << path);
513 return;
514 }
515
516 assert(s.writing());
517 assert(!s.reading());
518 freeChain(fileno, s, false);
519 debugs(54, 5, "closed idle entry " << fileno << " for reading " << path);
520}
521
50dc81ec 522bool
abf396ec 523Ipc::StoreMap::openForUpdating(Update &update, const sfileno fileNoHint)
50dc81ec 524{
abf396ec
AR
525 Must(update.entry);
526 const StoreEntry &entry = *update.entry;
527 const cache_key *const key = reinterpret_cast<const cache_key*>(entry.key);
528 update.stale.name = nameByKey(key);
529
530 if (!validEntry(fileNoHint)) {
531 debugs(54, 5, "opening entry with key " << storeKeyText(key) <<
532 " for updating " << path);
533 update.stale.fileNo = fileNoByName(update.stale.name);
534 } else {
535 update.stale.fileNo = fileNoHint;
536 }
537
538 debugs(54, 5, "opening entry " << update.stale.fileNo << " of " << entry << " for updating " << path);
539
540 // Unreadable entries cannot (e.g., empty and otherwise problematic entries)
541 // or should not (e.g., entries still forming their metadata) be updated.
b2aca62a 542 if (!openForReadingAt(update.stale.fileNo, key)) {
abf396ec
AR
543 debugs(54, 5, "cannot open unreadable entry " << update.stale.fileNo << " for updating " << path);
544 return false;
545 }
546
547 update.stale.anchor = &anchorAt(update.stale.fileNo);
548 if (update.stale.anchor->writing()) {
549 // TODO: Support updating appending entries.
550 // For example, MemStore::updateHeaders() would not know how
551 // many old prefix body bytes to copy to the new prefix if the last old
552 // prefix slice has not been formed yet (i.e., still gets more bytes).
553 debugs(54, 5, "cannot open appending entry " << update.stale.fileNo <<
554 " for updating " << path);
555 closeForReading(update.stale.fileNo);
556 return false;
557 }
558
559 if (!update.stale.anchor->lock.lockHeaders()) {
560 debugs(54, 5, "cannot open updating entry " << update.stale.fileNo <<
561 " for updating " << path);
562 closeForReading(update.stale.fileNo);
563 return false;
564 }
565
566 /* stale anchor is properly locked; we can now use abortUpdating() if needed */
567
568 if (!openKeyless(update.fresh)) {
569 debugs(54, 5, "cannot open freshchainless entry " << update.stale.fileNo <<
570 " for updating " << path);
571 abortUpdating(update);
572 return false;
573 }
574
575 Must(update.stale);
576 Must(update.fresh);
577 update.fresh.anchor->set(entry);
578 debugs(54, 5, "opened entry " << update.stale.fileNo << " for updating " << path <<
579 " using entry " << update.fresh.fileNo << " of " << entry);
580
581 return true;
582}
583
584/// finds an anchor that is currently not associated with any entry key and
585/// locks it for writing so ensure exclusive access during updates
586bool
587Ipc::StoreMap::openKeyless(Update::Edition &edition)
588{
589 return visitVictims([&](const sfileno name) {
590 Update::Edition temp;
591 temp.name = name;
592 temp.fileNo = fileNoByName(temp.name);
593 if ((temp.anchor = openForWritingAt(temp.fileNo))) {
594 debugs(54, 5, "created entry " << temp.fileNo <<
595 " for updating " << path);
596 Must(temp);
597 edition = temp;
598 return true;
599 }
600 return false;
601 });
602}
603
604void
605Ipc::StoreMap::closeForUpdating(Update &update)
606{
607 Must(update.stale.anchor);
608 Must(update.fresh.anchor);
609 AssertFlagIsSet(update.stale.anchor->lock.updating);
610 Must(update.stale.splicingPoint >= 0);
611 Must(update.fresh.splicingPoint >= 0);
612
613 /* the stale prefix cannot overlap with the fresh one (a weak check) */
614 Must(update.stale.anchor->start != update.fresh.anchor->start);
615 Must(update.stale.anchor->start != update.fresh.splicingPoint);
616 Must(update.stale.splicingPoint != update.fresh.anchor->start);
617 Must(update.stale.splicingPoint != update.fresh.splicingPoint);
618
619 /* the relative order of most operations is significant here */
620
621 /* splice the fresh chain prefix with the stale chain suffix */
622 Slice &freshSplicingSlice = sliceAt(update.fresh.splicingPoint);
623 const SliceId suffixStart = sliceAt(update.stale.splicingPoint).next; // may be negative
624 // the fresh chain is either properly terminated or already spliced
625 if (freshSplicingSlice.next < 0)
626 freshSplicingSlice.next = suffixStart;
627 else
628 Must(freshSplicingSlice.next == suffixStart);
629 // either way, fresh chain uses the stale chain suffix now
630
631 // make the fresh anchor/chain readable for everybody
632 update.fresh.anchor->lock.switchExclusiveToShared();
633 // but the fresh anchor is still invisible to anybody but us
634
635 // This freeEntry() code duplicates the code below to minimize the time when
636 // the freeEntry() race condition (see the Race: comment below) might occur.
637 if (update.stale.anchor->waitingToBeFreed)
638 freeEntry(update.fresh.fileNo);
639
640 /* any external changes were applied to the stale anchor/chain until now */
641 relocate(update.stale.name, update.fresh.fileNo);
642 /* any external changes will apply to the fresh anchor/chain from now on */
643
644 // Race: If the stale entry was deleted by some kid during the assignment,
645 // then we propagate that event to the fresh anchor and chain. Since this
646 // update is not atomically combined with the assignment above, another kid
647 // might get a fresh entry just before we have a chance to free it. However,
648 // such deletion races are always possible even without updates.
649 if (update.stale.anchor->waitingToBeFreed)
650 freeEntry(update.fresh.fileNo);
651
652 /* free the stale chain prefix except for the shared suffix */
653 update.stale.anchor->splicingPoint = update.stale.splicingPoint;
654 freeEntry(update.stale.fileNo);
655
66d51f4f
AR
656 // Make the stale anchor/chain reusable, reachable via update.fresh.name. If
657 // update.entry->swap_filen is still update.stale.fileNo, and the entry is
658 // using store, then the entry must have a lock on update.stale.fileNo,
659 // preventing its premature reuse by others.
abf396ec
AR
660 relocate(update.fresh.name, update.stale.fileNo);
661
662 const Update updateSaved = update; // for post-close debugging below
663
664 /* unlock the stale anchor/chain */
665 update.stale.anchor->lock.unlockHeaders();
666 closeForReading(update.stale.fileNo);
667 update.stale = Update::Edition();
668
669 // finally, unlock the fresh entry
670 closeForReading(update.fresh.fileNo);
671 update.fresh = Update::Edition();
672
673 debugs(54, 5, "closed entry " << updateSaved.stale.fileNo << " of " << *updateSaved.entry <<
674 " named " << updateSaved.stale.name << " for updating " << path <<
675 " to fresh entry " << updateSaved.fresh.fileNo << " named " << updateSaved.fresh.name <<
676 " with [" << updateSaved.fresh.anchor->start << ',' << updateSaved.fresh.splicingPoint <<
677 "] prefix containing at least " << freshSplicingSlice.size << " bytes");
678}
679
680/// Visits entries until either
681/// * the `visitor` returns true (indicating its satisfaction with the offer);
682/// * we give up finding a suitable entry because it already took "too long"; or
683/// * we have offered all entries.
684bool
685Ipc::StoreMap::visitVictims(const NameFilter visitor)
686{
687 // Hopefully, we find a usable entry much sooner (TODO: use time?).
50dc81ec
AR
688 // The min() will protect us from division by zero inside the loop.
689 const int searchLimit = min(10000, entryLimit());
690 int tries = 0;
691 for (; tries < searchLimit; ++tries) {
abf396ec
AR
692 const sfileno name = static_cast<sfileno>(++anchors->victim % entryLimit());
693 if (visitor(name))
694 return true;
695 }
696
697 debugs(54, 5, "no victims found in " << path << "; tried: " << tries);
698 return false;
699}
700
701bool
702Ipc::StoreMap::purgeOne()
703{
704 return visitVictims([&](const sfileno name) {
705 const sfileno fileno = fileNoByName(name);
1860fbac 706 Anchor &s = anchorAt(fileno);
50dc81ec 707 if (s.lock.lockExclusive()) {
5bba33c9
AR
708 // the caller wants a free slice; empty anchor is not enough
709 if (!s.empty() && s.start >= 0) {
50dc81ec
AR
710 // this entry may be marked for deletion, and that is OK
711 freeChain(fileno, s, false);
6d68a230 712 debugs(54, 5, "purged entry " << fileno << " from " << path);
50dc81ec 713 return true;
6d68a230 714 }
50dc81ec
AR
715 s.lock.unlockExclusive();
716 }
abf396ec
AR
717 return false;
718 });
50dc81ec
AR
719}
720
721void
722Ipc::StoreMap::importSlice(const SliceId sliceId, const Slice &slice)
723{
724 // Slices are imported into positions that should not be available via
725 // "get free slice" API. This is not something we can double check
726 // reliably because the anchor for the imported slice may not have been
727 // imported yet.
36c84e19 728 assert(validSlice(sliceId));
1860fbac 729 sliceAt(sliceId) = slice;
44c95fcf
AR
730}
731
732int
733Ipc::StoreMap::entryLimit() const
734{
36c84e19 735 return min(sliceLimit(), static_cast<int>(SwapFilenMax+1));
44c95fcf
AR
736}
737
738int
739Ipc::StoreMap::entryCount() const
740{
1860fbac 741 return anchors->count;
36c84e19
AR
742}
743
744int
745Ipc::StoreMap::sliceLimit() const
746{
1860fbac 747 return slices->capacity;
44c95fcf
AR
748}
749
44c95fcf
AR
750void
751Ipc::StoreMap::updateStats(ReadWriteLockStats &stats) const
752{
1860fbac
AR
753 for (int i = 0; i < anchors->capacity; ++i)
754 anchorAt(i).lock.updateStats(stats);
44c95fcf
AR
755}
756
757bool
36c84e19 758Ipc::StoreMap::validEntry(const int pos) const
44c95fcf
AR
759{
760 return 0 <= pos && pos < entryLimit();
761}
762
36c84e19
AR
763bool
764Ipc::StoreMap::validSlice(const int pos) const
765{
766 return 0 <= pos && pos < sliceLimit();
767}
768
b2aca62a
EB
769/// Checks whether the object lifetime has exceeded the specified maximum.
770/// The lifetime is considered to exceed the maximum if the time goes backwards.
771/// Uses the highest precision provided by the C++ implementation.
772class ConservativeTimer
773{
774public:
775 typedef std::chrono::high_resolution_clock Clock;
776
777 explicit ConservativeTimer(const Clock::duration max):
778 startTime(Clock::now()),
779 lastTime(startTime),
780 maxTime(startTime + max) {}
781
782 /// whether the current time reached the provided maximum time
783 bool expired() {
784 const auto currentTime = Clock::now();
785 if (currentTime < lastTime) // time went backwards
786 return true;
787 lastTime = currentTime;
788 return lastTime > maxTime;
789 }
790
791private:
792 /// the object creation time
793 Clock::time_point startTime;
794 /// the time of the last expired() call, initially equals to startTime
795 Clock::time_point lastTime;
796 /// after going past this point in time, expired() becomes true
797 const Clock::time_point maxTime;
798};
799
800bool
801Ipc::StoreMap::validateHit(const sfileno fileno)
802{
803 ConservativeTimer timer(Config.paranoid_hit_validation);
804 const auto timeIsLimited = Config.paranoid_hit_validation < std::chrono::hours(24);
805
806 const auto &anchor = anchorAt(fileno);
807
808 ++statCounter.hitValidation.attempts;
809
810 if (!anchor.basics.swap_file_sz) {
811 ++statCounter.hitValidation.refusalsDueToZeroSize;
812 return true; // presume valid; cannot validate w/o known swap_file_sz
813 }
814
815 if (!anchor.lock.lockHeaders()) {
816 ++statCounter.hitValidation.refusalsDueToLocking;
817 return true; // presume valid; cannot validate changing entry
818 }
819
820 const uint64_t expectedByteCount = anchor.basics.swap_file_sz;
821
822 size_t actualSliceCount = 0;
823 uint64_t actualByteCount = 0;
824 SliceId lastSeenSlice = anchor.start;
825 while (lastSeenSlice >= 0) {
826 ++actualSliceCount;
827 if (!validSlice(lastSeenSlice))
828 break;
829 const auto &slice = sliceAt(lastSeenSlice);
830 actualByteCount += slice.size;
831 if (actualByteCount > expectedByteCount)
832 break;
833 lastSeenSlice = slice.next;
834 if (timeIsLimited && timer.expired()) {
835 anchor.lock.unlockHeaders();
836 ++statCounter.hitValidation.refusalsDueToTimeLimit;
837 return true;
838 }
839 }
840
841 anchor.lock.unlockHeaders();
842
843 if (actualByteCount == expectedByteCount && lastSeenSlice < 0)
844 return true;
845
846 ++statCounter.hitValidation.failures;
847
848 debugs(54, DBG_IMPORTANT, "BUG: purging corrupted cache entry " << fileno <<
849 " from " << path <<
850 " expected swap_file_sz=" << expectedByteCount <<
851 " actual swap_file_sz=" << actualByteCount <<
852 " actual slices=" << actualSliceCount <<
853 " last slice seen=" << lastSeenSlice << "\n" <<
854 " key=" << storeKeyText(reinterpret_cast<const cache_key*>(anchor.key)) << "\n" <<
855 " tmestmp=" << anchor.basics.timestamp << "\n" <<
856 " lastref=" << anchor.basics.lastref << "\n" <<
857 " expires=" << anchor.basics.expires << "\n" <<
858 " lastmod=" << anchor.basics.lastmod << "\n" <<
859 " refcount=" << anchor.basics.refcount << "\n" <<
860 " flags=0x" << std::hex << anchor.basics.flags << std::dec << "\n" <<
861 " start=" << anchor.start << "\n" <<
862 " splicingPoint=" << anchor.splicingPoint << "\n" <<
863 " lock=" << anchor.lock << "\n" <<
864 " waitingToBeFreed=" << (anchor.waitingToBeFreed ? 1 : 0) << "\n"
865 );
866 freeEntry(fileno);
867 return false;
868}
869
1860fbac 870Ipc::StoreMap::Anchor&
2da4bfe6
A
871Ipc::StoreMap::anchorAt(const sfileno fileno)
872{
1860fbac
AR
873 assert(validEntry(fileno));
874 return anchors->items[fileno];
875}
876
877const Ipc::StoreMap::Anchor&
2da4bfe6
A
878Ipc::StoreMap::anchorAt(const sfileno fileno) const
879{
1860fbac
AR
880 return const_cast<StoreMap&>(*this).anchorAt(fileno);
881}
882
50dc81ec 883sfileno
abf396ec 884Ipc::StoreMap::nameByKey(const cache_key *const key) const
44c95fcf 885{
4310f8b0 886 assert(key);
44c95fcf
AR
887 const uint64_t *const k = reinterpret_cast<const uint64_t *>(key);
888 // TODO: use a better hash function
abf396ec
AR
889 const int hash = (k[0] + k[1]) % entryLimit();
890 return hash;
891}
892
893sfileno
894Ipc::StoreMap::fileNoByName(const sfileno name) const
895{
896 // fileNos->items are initialized to zero, which we treat as "name is fileno";
897 // a positive value means the entry anchor got moved to a new fileNo
898 if (const int item = fileNos->items[name])
899 return item-1;
900 return name;
901}
902
903/// map `name` to `fileNo`
904void
905Ipc::StoreMap::relocate(const sfileno name, const sfileno fileno)
906{
907 // preserve special meaning for zero; see fileNoByName
908 fileNos->items[name] = fileno+1;
909}
910
911sfileno
912Ipc::StoreMap::fileNoByKey(const cache_key *const key) const
913{
914 const int name = nameByKey(key);
915 return fileNoByName(name);
44c95fcf
AR
916}
917
50dc81ec
AR
918Ipc::StoreMap::Anchor &
919Ipc::StoreMap::anchorByKey(const cache_key *const key)
44c95fcf 920{
abf396ec 921 return anchorAt(fileNoByKey(key));
1860fbac
AR
922}
923
924Ipc::StoreMap::Slice&
2da4bfe6
A
925Ipc::StoreMap::sliceAt(const SliceId sliceId)
926{
1860fbac
AR
927 assert(validSlice(sliceId));
928 return slices->items[sliceId];
929}
930
931const Ipc::StoreMap::Slice&
2da4bfe6
A
932Ipc::StoreMap::sliceAt(const SliceId sliceId) const
933{
1860fbac 934 return const_cast<StoreMap&>(*this).sliceAt(sliceId);
44c95fcf
AR
935}
936
50dc81ec 937/* Ipc::StoreMapAnchor */
44c95fcf 938
abf396ec 939Ipc::StoreMapAnchor::StoreMapAnchor(): start(0), splicingPoint(-1)
44c95fcf 940{
50dc81ec 941 // keep in sync with rewind()
44c95fcf
AR
942}
943
944void
50dc81ec 945Ipc::StoreMapAnchor::setKey(const cache_key *const aKey)
44c95fcf
AR
946{
947 memcpy(key, aKey, sizeof(key));
4310f8b0 948 waitingToBeFreed = Store::Root().markedForDeletion(aKey);
44c95fcf
AR
949}
950
951bool
50dc81ec 952Ipc::StoreMapAnchor::sameKey(const cache_key *const aKey) const
44c95fcf
AR
953{
954 const uint64_t *const k = reinterpret_cast<const uint64_t *>(aKey);
955 return k[0] == key[0] && k[1] == key[1];
956}
957
958void
4310f8b0 959Ipc::StoreMapAnchor::set(const StoreEntry &from, const cache_key *aKey)
44c95fcf 960{
ce49546e 961 assert(writing() && !reading());
4310f8b0 962 setKey(reinterpret_cast<const cache_key*>(aKey ? aKey : from.key));
44c95fcf
AR
963 basics.timestamp = from.timestamp;
964 basics.lastref = from.lastref;
965 basics.expires = from.expires;
438b41ba 966 basics.lastmod = from.lastModified();
44c95fcf
AR
967 basics.swap_file_sz = from.swap_file_sz;
968 basics.refcount = from.refcount;
4310f8b0
EB
969
970 // do not copy key bit if we are not using from.key
971 // TODO: Replace KEY_PRIVATE with a nil StoreEntry::key!
972 uint16_t cleanFlags = from.flags;
973 if (aKey)
974 EBIT_CLR(cleanFlags, KEY_PRIVATE);
975 basics.flags = cleanFlags;
976}
977
978void
979Ipc::StoreMapAnchor::exportInto(StoreEntry &into) const
980{
981 assert(reading());
982 into.timestamp = basics.timestamp;
983 into.lastref = basics.lastref;
984 into.expires = basics.expires;
985 into.lastModified(basics.lastmod);
986 into.swap_file_sz = basics.swap_file_sz;
987 into.refcount = basics.refcount;
d2a6dcba 988 const bool collapsingRequired = into.hittingRequiresCollapsing();
4310f8b0 989 into.flags = basics.flags;
d2a6dcba
EB
990 // There are possibly several flags we do not need to overwrite,
991 // and ENTRY_REQUIRES_COLLAPSING is one of them.
992 // TODO: check for other flags.
993 into.setCollapsingRequirement(collapsingRequired);
44c95fcf
AR
994}
995
50dc81ec
AR
996void
997Ipc::StoreMapAnchor::rewind()
998{
ce49546e 999 assert(writing());
50dc81ec 1000 start = 0;
abf396ec 1001 splicingPoint = -1;
50dc81ec 1002 memset(&key, 0, sizeof(key));
b56b37cf 1003 basics.clear();
abf396ec 1004 waitingToBeFreed = false;
4310f8b0 1005 writerHalted = false;
50dc81ec
AR
1006 // but keep the lock
1007}
1008
abf396ec
AR
1009/* Ipc::StoreMapUpdate */
1010
1011Ipc::StoreMapUpdate::StoreMapUpdate(StoreEntry *anEntry):
1012 entry(anEntry)
1013{
1014 entry->lock("Ipc::StoreMapUpdate1");
1015}
1016
1017Ipc::StoreMapUpdate::StoreMapUpdate(const StoreMapUpdate &other):
1018 entry(other.entry),
1019 stale(other.stale),
1020 fresh(other.fresh)
1021{
1022 entry->lock("Ipc::StoreMapUpdate2");
1023}
1024
1025Ipc::StoreMapUpdate::~StoreMapUpdate()
1026{
1027 entry->unlock("Ipc::StoreMapUpdate");
1028}
1029
1030/* Ipc::StoreMap::Owner */
1031
1032Ipc::StoreMap::Owner::Owner():
1033 fileNos(nullptr),
1034 anchors(nullptr),
1035 slices(nullptr)
1860fbac
AR
1036{
1037}
1038
1039Ipc::StoreMap::Owner::~Owner()
1040{
abf396ec 1041 delete fileNos;
1860fbac
AR
1042 delete anchors;
1043 delete slices;
1044}
1045
1046/* Ipc::StoreMapAnchors */
44c95fcf 1047
1860fbac 1048Ipc::StoreMapAnchors::StoreMapAnchors(const int aCapacity):
f53969cc
SM
1049 count(0),
1050 victim(0),
1051 capacity(aCapacity),
1052 items(aCapacity)
68353d5a
DK
1053{
1054}
1055
1056size_t
1860fbac 1057Ipc::StoreMapAnchors::sharedMemorySize() const
44c95fcf 1058{
1860fbac 1059 return SharedMemorySize(capacity);
44c95fcf
AR
1060}
1061
1062size_t
1860fbac 1063Ipc::StoreMapAnchors::SharedMemorySize(const int capacity)
44c95fcf 1064{
1860fbac 1065 return sizeof(StoreMapAnchors) + capacity * sizeof(StoreMapAnchor);
44c95fcf
AR
1066}
1067