]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ipc/StoreMap.cc
Document counter-intuitive round-robin cache_dir selection bias; decrease it.
[thirdparty/squid.git] / src / ipc / StoreMap.cc
CommitLineData
44c95fcf 1/*
44c95fcf
AR
2 * DEBUG: section 54 Interprocess Communication
3 */
4
582c2af2 5#include "squid.h"
5bed43d6 6#include "ipc/StoreMap.h"
1860fbac 7#include "SBuf.h"
5bed43d6 8#include "Store.h"
602d9612 9#include "store_key_md5.h"
5bed43d6 10#include "tools.h"
44c95fcf 11
1860fbac
AR
12static SBuf
13StoreMapSlicesId(const SBuf &path)
44c95fcf 14{
1860fbac 15 return Ipc::Mem::Segment::Name(path, "slices");
44c95fcf
AR
16}
17
1860fbac
AR
18static SBuf
19StoreMapAnchorsId(const SBuf &path)
44c95fcf 20{
1860fbac 21 return Ipc::Mem::Segment::Name(path, "anchors");
44c95fcf
AR
22}
23
1860fbac
AR
24Ipc::StoreMap::Owner *
25Ipc::StoreMap::Init(const SBuf &path, const int sliceLimit)
26{
27 assert(sliceLimit > 0); // we should not be created otherwise
28 const int anchorLimit = min(sliceLimit, static_cast<int>(SwapFilenMax));
29 Owner *owner = new Owner;
30 owner->anchors = shm_new(Anchors)(StoreMapAnchorsId(path).c_str(), anchorLimit);
31 owner->slices = shm_new(Slices)(StoreMapSlicesId(path).c_str(), sliceLimit);
32 debugs(54, 5, "created " << path << " with " << anchorLimit << '+' << sliceLimit);
33 return owner;
34}
35
36Ipc::StoreMap::StoreMap(const SBuf &aPath): cleaner(NULL), path(aPath),
37 anchors(shm_old(Anchors)(StoreMapAnchorsId(path).c_str())),
38 slices(shm_old(Slices)(StoreMapSlicesId(path).c_str()))
c011f9bc 39{
1860fbac
AR
40 debugs(54, 5, "attached " << path << " with " <<
41 anchors->capacity << '+' << slices->capacity);
42 assert(entryLimit() > 0); // key-to-position mapping requires this
43 assert(entryLimit() <= sliceLimit()); // at least one slice per entry
c011f9bc
DK
44}
45
50dc81ec
AR
46int
47Ipc::StoreMap::compareVersions(const sfileno fileno, time_t newVersion) const
48{
1860fbac 49 const Anchor &inode = anchorAt(fileno);
50dc81ec
AR
50
51 // note: we do not lock, so comparison may be inacurate
52
ce49546e 53 if (inode.empty())
50dc81ec
AR
54 return +2;
55
56 if (const time_t diff = newVersion - inode.basics.timestamp)
57 return diff < 0 ? -1 : +1;
9d4e9cfb 58
50dc81ec
AR
59 return 0;
60}
61
62void
63Ipc::StoreMap::forgetWritingEntry(sfileno fileno)
64{
1860fbac 65 Anchor &inode = anchorAt(fileno);
50dc81ec 66
ce49546e 67 assert(inode.writing());
50dc81ec
AR
68
69 // we do not iterate slices because we were told to forget about
70 // them; the caller is responsible for freeing them (most likely
71 // our slice list is incomplete or has holes)
72
73 inode.waitingToBeFreed = false;
ce49546e 74 inode.rewind();
50dc81ec
AR
75
76 inode.lock.unlockExclusive();
1860fbac 77 anchors->count;
50dc81ec
AR
78
79 debugs(54, 8, "closed entry " << fileno << " for writing " << path);
80}
81
82Ipc::StoreMap::Anchor *
44c95fcf
AR
83Ipc::StoreMap::openForWriting(const cache_key *const key, sfileno &fileno)
84{
50dc81ec 85 debugs(54, 5, "opening entry with key " << storeKeyText(key)
6d68a230 86 << " for writing " << path);
50dc81ec 87 const int idx = anchorIndexByKey(key);
44c95fcf 88
50dc81ec
AR
89 if (Anchor *anchor = openForWritingAt(idx)) {
90 fileno = idx;
91 return anchor;
92 }
93
94 return NULL;
95}
96
97Ipc::StoreMap::Anchor *
98Ipc::StoreMap::openForWritingAt(const sfileno fileno, bool overwriteExisting)
99{
1860fbac 100 Anchor &s = anchorAt(fileno);
44c95fcf
AR
101 ReadWriteLock &lock = s.lock;
102
103 if (lock.lockExclusive()) {
ce49546e 104 assert(s.writing() && !s.reading());
50dc81ec
AR
105
106 // bail if we cannot empty this position
ce49546e 107 if (!s.waitingToBeFreed && !s.empty() && !overwriteExisting) {
50dc81ec 108 lock.unlockExclusive();
6d68a230
AR
109 debugs(54, 5, "cannot open existing entry " << fileno <<
110 " for writing " << path);
50dc81ec
AR
111 return NULL;
112 }
44c95fcf 113
7f6748c8 114 // free if the entry was used, keeping the entry locked
ce49546e 115 if (s.waitingToBeFreed || !s.empty())
50dc81ec 116 freeChain(fileno, s, true);
44c95fcf 117
ce49546e 118 assert(s.empty());
a3023c03 119 s.start = -1; // we have not allocated any slices yet
1860fbac 120 ++anchors->count;
50dc81ec 121
44c95fcf 122 //s.setKey(key); // XXX: the caller should do that
50dc81ec 123 debugs(54, 5, "opened entry " << fileno << " for writing " << path);
44c95fcf 124 return &s; // and keep the entry locked
6d68a230 125 }
44c95fcf 126
6d68a230
AR
127 debugs(54, 5, "cannot open busy entry " << fileno <<
128 " for writing " << path);
44c95fcf
AR
129 return NULL;
130}
131
ce49546e
AR
132void
133Ipc::StoreMap::startAppending(const sfileno fileno)
134{
1860fbac 135 Anchor &s = anchorAt(fileno);
ce49546e
AR
136 assert(s.writing());
137 s.lock.startAppending();
138 debugs(54, 5, "restricted entry " << fileno << " to appending " << path);
139}
140
44c95fcf
AR
141void
142Ipc::StoreMap::closeForWriting(const sfileno fileno, bool lockForReading)
143{
1860fbac 144 Anchor &s = anchorAt(fileno);
ce49546e 145 assert(s.writing());
50dc81ec 146 if (lockForReading) {
44c95fcf 147 s.lock.switchExclusiveToShared();
6d68a230
AR
148 debugs(54, 5, "switched entry " << fileno <<
149 " from writing to reading " << path);
ce49546e 150 assert(s.complete());
50dc81ec 151 } else {
44c95fcf 152 s.lock.unlockExclusive();
50dc81ec 153 debugs(54, 5, "closed entry " << fileno << " for writing " << path);
ce49546e 154 // cannot assert completeness here because we have no lock
50dc81ec
AR
155 }
156}
157
158Ipc::StoreMap::Slice &
159Ipc::StoreMap::writeableSlice(const AnchorId anchorId, const SliceId sliceId)
160{
1860fbac 161 assert(anchorAt(anchorId).writing());
36c84e19 162 assert(validSlice(sliceId));
1860fbac 163 return sliceAt(sliceId);
50dc81ec
AR
164}
165
166const Ipc::StoreMap::Slice &
167Ipc::StoreMap::readableSlice(const AnchorId anchorId, const SliceId sliceId) const
168{
1860fbac 169 assert(anchorAt(anchorId).reading());
36c84e19 170 assert(validSlice(sliceId));
1860fbac 171 return sliceAt(sliceId);
50dc81ec
AR
172}
173
174Ipc::StoreMap::Anchor &
175Ipc::StoreMap::writeableEntry(const AnchorId anchorId)
176{
1860fbac
AR
177 assert(anchorAt(anchorId).writing());
178 return anchorAt(anchorId);
ce49546e
AR
179}
180
181const Ipc::StoreMap::Anchor &
182Ipc::StoreMap::readableEntry(const AnchorId anchorId) const
183{
1860fbac
AR
184 assert(anchorAt(anchorId).reading());
185 return anchorAt(anchorId);
44c95fcf
AR
186}
187
44c95fcf
AR
188void
189Ipc::StoreMap::abortWriting(const sfileno fileno)
190{
6d68a230 191 debugs(54, 5, "aborting entry " << fileno << " for writing " << path);
1860fbac 192 Anchor &s = anchorAt(fileno);
ce49546e
AR
193 assert(s.writing());
194 s.lock.appending = false; // locks out any new readers
195 if (!s.lock.readers) {
196 freeChain(fileno, s, false);
197 debugs(54, 5, "closed clean entry " << fileno << " for writing " << path);
198 } else {
199 s.waitingToBeFreed = true;
ce49546e
AR
200 s.lock.unlockExclusive();
201 debugs(54, 5, "closed dirty entry " << fileno << " for writing " << path);
9d4e9cfb 202 }
44c95fcf
AR
203}
204
50dc81ec 205const Ipc::StoreMap::Anchor *
44c95fcf
AR
206Ipc::StoreMap::peekAtReader(const sfileno fileno) const
207{
1860fbac 208 const Anchor &s = anchorAt(fileno);
ce49546e 209 if (s.reading())
44c95fcf 210 return &s; // immediate access by lock holder so no locking
ce49546e
AR
211 if (s.writing())
212 return NULL; // the caller is not a read lock holder
213 assert(false); // must be locked for reading or writing
44c95fcf
AR
214 return NULL;
215}
216
d366a7fa
AR
217const Ipc::StoreMap::Anchor &
218Ipc::StoreMap::peekAtEntry(const sfileno fileno) const
219{
1860fbac 220 return anchorAt(fileno);
d366a7fa
AR
221}
222
44c95fcf 223void
50dc81ec 224Ipc::StoreMap::freeEntry(const sfileno fileno)
44c95fcf 225{
6d68a230 226 debugs(54, 5, "marking entry " << fileno << " to be freed in " << path);
44c95fcf 227
1860fbac 228 Anchor &s = anchorAt(fileno);
44c95fcf
AR
229
230 if (s.lock.lockExclusive())
50dc81ec 231 freeChain(fileno, s, false);
44c95fcf
AR
232 else
233 s.waitingToBeFreed = true; // mark to free it later
234}
235
ce49546e
AR
236void
237Ipc::StoreMap::freeEntryByKey(const cache_key *const key)
238{
239 debugs(54, 5, "marking entry with key " << storeKeyText(key)
240 << " to be freed in " << path);
241
242 const int idx = anchorIndexByKey(key);
1860fbac 243 Anchor &s = anchorAt(idx);
ce49546e
AR
244 if (s.lock.lockExclusive()) {
245 if (s.sameKey(key))
246 freeChain(idx, s, true);
247 s.lock.unlockExclusive();
9d4e9cfb 248 } else if (s.lock.lockShared()) {
ce49546e
AR
249 if (s.sameKey(key))
250 s.waitingToBeFreed = true; // mark to free it later
251 s.lock.unlockShared();
252 } else {
9d4e9cfb
AR
253 // we cannot be sure that the entry we found is ours because we do not
254 // have a lock on it, but we still check to minimize false deletions
255 if (s.sameKey(key))
256 s.waitingToBeFreed = true; // mark to free it later
ce49546e
AR
257 }
258}
259
50dc81ec
AR
260/// unconditionally frees an already locked chain of slots, unlocking if needed
261void
262Ipc::StoreMap::freeChain(const sfileno fileno, Anchor &inode, const bool keepLocked)
263{
ce49546e 264 debugs(54, 7, "freeing entry " << fileno <<
6d68a230 265 " in " << path);
ce49546e 266 if (!inode.empty()) {
50dc81ec 267 sfileno sliceId = inode.start;
6d68a230 268 debugs(54, 8, "first slice " << sliceId);
50dc81ec 269 while (sliceId >= 0) {
1860fbac 270 Slice &slice = sliceAt(sliceId);
485466dc 271 const sfileno nextId = slice.next;
a3023c03 272 slice.size = 0;
485466dc
AR
273 slice.next = -1;
274 if (cleaner)
275 cleaner->noteFreeMapSlice(sliceId); // might change slice state
50dc81ec
AR
276 sliceId = nextId;
277 }
278 }
279
280 inode.waitingToBeFreed = false;
ce49546e 281 inode.rewind();
50dc81ec
AR
282
283 if (!keepLocked)
284 inode.lock.unlockExclusive();
1860fbac 285 --anchors->count;
6d68a230 286 debugs(54, 5, "freed entry " << fileno << " in " << path);
50dc81ec
AR
287}
288
289const Ipc::StoreMap::Anchor *
44c95fcf
AR
290Ipc::StoreMap::openForReading(const cache_key *const key, sfileno &fileno)
291{
50dc81ec 292 debugs(54, 5, "opening entry with key " << storeKeyText(key)
6d68a230 293 << " for reading " << path);
50dc81ec
AR
294 const int idx = anchorIndexByKey(key);
295 if (const Anchor *slot = openForReadingAt(idx)) {
44c95fcf
AR
296 if (slot->sameKey(key)) {
297 fileno = idx;
44c95fcf
AR
298 return slot; // locked for reading
299 }
300 slot->lock.unlockShared();
50dc81ec 301 debugs(54, 7, "closed entry " << idx << " for reading " << path);
44c95fcf 302 }
44c95fcf
AR
303 return NULL;
304}
305
50dc81ec 306const Ipc::StoreMap::Anchor *
44c95fcf
AR
307Ipc::StoreMap::openForReadingAt(const sfileno fileno)
308{
6d68a230 309 debugs(54, 5, "opening entry " << fileno << " for reading " << path);
1860fbac 310 Anchor &s = anchorAt(fileno);
44c95fcf
AR
311
312 if (!s.lock.lockShared()) {
6d68a230
AR
313 debugs(54, 5, "cannot open busy entry " << fileno <<
314 " for reading " << path);
44c95fcf
AR
315 return NULL;
316 }
317
ce49546e 318 if (s.empty()) {
44c95fcf 319 s.lock.unlockShared();
6d68a230
AR
320 debugs(54, 7, "cannot open empty entry " << fileno <<
321 " for reading " << path);
44c95fcf
AR
322 return NULL;
323 }
324
325 if (s.waitingToBeFreed) {
326 s.lock.unlockShared();
539283df 327 debugs(54, 7, "cannot open marked entry " << fileno <<
6d68a230 328 " for reading " << path);
44c95fcf
AR
329 return NULL;
330 }
331
50dc81ec 332 debugs(54, 5, "opened entry " << fileno << " for reading " << path);
44c95fcf
AR
333 return &s;
334}
335
336void
337Ipc::StoreMap::closeForReading(const sfileno fileno)
338{
1860fbac 339 Anchor &s = anchorAt(fileno);
ce49546e 340 assert(s.reading());
44c95fcf 341 s.lock.unlockShared();
50dc81ec
AR
342 debugs(54, 5, "closed entry " << fileno << " for reading " << path);
343}
344
345bool
346Ipc::StoreMap::purgeOne()
347{
348 // Hopefully, we find a removable entry much sooner (TODO: use time?).
349 // The min() will protect us from division by zero inside the loop.
350 const int searchLimit = min(10000, entryLimit());
351 int tries = 0;
352 for (; tries < searchLimit; ++tries) {
1860fbac
AR
353 const sfileno fileno = static_cast<sfileno>(++anchors->victim % entryLimit());
354 Anchor &s = anchorAt(fileno);
50dc81ec 355 if (s.lock.lockExclusive()) {
5bba33c9
AR
356 // the caller wants a free slice; empty anchor is not enough
357 if (!s.empty() && s.start >= 0) {
50dc81ec
AR
358 // this entry may be marked for deletion, and that is OK
359 freeChain(fileno, s, false);
6d68a230 360 debugs(54, 5, "purged entry " << fileno << " from " << path);
50dc81ec 361 return true;
6d68a230 362 }
50dc81ec
AR
363 s.lock.unlockExclusive();
364 }
6d68a230
AR
365 }
366 debugs(54, 5, "no entries to purge from " << path << "; tried: " << tries);
50dc81ec
AR
367 return false;
368}
369
370void
371Ipc::StoreMap::importSlice(const SliceId sliceId, const Slice &slice)
372{
373 // Slices are imported into positions that should not be available via
374 // "get free slice" API. This is not something we can double check
375 // reliably because the anchor for the imported slice may not have been
376 // imported yet.
36c84e19 377 assert(validSlice(sliceId));
1860fbac 378 sliceAt(sliceId) = slice;
44c95fcf
AR
379}
380
381int
382Ipc::StoreMap::entryLimit() const
383{
36c84e19 384 return min(sliceLimit(), static_cast<int>(SwapFilenMax+1));
44c95fcf
AR
385}
386
387int
388Ipc::StoreMap::entryCount() const
389{
1860fbac 390 return anchors->count;
36c84e19
AR
391}
392
393int
394Ipc::StoreMap::sliceLimit() const
395{
1860fbac 396 return slices->capacity;
44c95fcf
AR
397}
398
44c95fcf
AR
399void
400Ipc::StoreMap::updateStats(ReadWriteLockStats &stats) const
401{
1860fbac
AR
402 for (int i = 0; i < anchors->capacity; ++i)
403 anchorAt(i).lock.updateStats(stats);
44c95fcf
AR
404}
405
406bool
36c84e19 407Ipc::StoreMap::validEntry(const int pos) const
44c95fcf
AR
408{
409 return 0 <= pos && pos < entryLimit();
410}
411
36c84e19
AR
412bool
413Ipc::StoreMap::validSlice(const int pos) const
414{
415 return 0 <= pos && pos < sliceLimit();
416}
417
1860fbac
AR
418Ipc::StoreMap::Anchor&
419Ipc::StoreMap::anchorAt(const sfileno fileno) {
420 assert(validEntry(fileno));
421 return anchors->items[fileno];
422}
423
424const Ipc::StoreMap::Anchor&
425Ipc::StoreMap::anchorAt(const sfileno fileno) const {
426 return const_cast<StoreMap&>(*this).anchorAt(fileno);
427}
428
50dc81ec
AR
429sfileno
430Ipc::StoreMap::anchorIndexByKey(const cache_key *const key) const
44c95fcf
AR
431{
432 const uint64_t *const k = reinterpret_cast<const uint64_t *>(key);
433 // TODO: use a better hash function
36c84e19 434 return (k[0] + k[1]) % entryLimit();
44c95fcf
AR
435}
436
50dc81ec
AR
437Ipc::StoreMap::Anchor &
438Ipc::StoreMap::anchorByKey(const cache_key *const key)
44c95fcf 439{
1860fbac
AR
440 return anchorAt(anchorIndexByKey(key));
441}
442
443Ipc::StoreMap::Slice&
444Ipc::StoreMap::sliceAt(const SliceId sliceId) {
445 assert(validSlice(sliceId));
446 return slices->items[sliceId];
447}
448
449const Ipc::StoreMap::Slice&
450Ipc::StoreMap::sliceAt(const SliceId sliceId) const {
451 return const_cast<StoreMap&>(*this).sliceAt(sliceId);
44c95fcf
AR
452}
453
50dc81ec 454/* Ipc::StoreMapAnchor */
44c95fcf 455
ce49546e 456Ipc::StoreMapAnchor::StoreMapAnchor(): start(0)
44c95fcf 457{
e297be13
AJ
458 memset(&key, 0, sizeof(key));
459 memset(&basics, 0, sizeof(basics));
50dc81ec 460 // keep in sync with rewind()
44c95fcf
AR
461}
462
463void
50dc81ec 464Ipc::StoreMapAnchor::setKey(const cache_key *const aKey)
44c95fcf
AR
465{
466 memcpy(key, aKey, sizeof(key));
467}
468
469bool
50dc81ec 470Ipc::StoreMapAnchor::sameKey(const cache_key *const aKey) const
44c95fcf
AR
471{
472 const uint64_t *const k = reinterpret_cast<const uint64_t *>(aKey);
473 return k[0] == key[0] && k[1] == key[1];
474}
475
476void
50dc81ec 477Ipc::StoreMapAnchor::set(const StoreEntry &from)
44c95fcf 478{
ce49546e 479 assert(writing() && !reading());
44c95fcf 480 memcpy(key, from.key, sizeof(key));
44c95fcf
AR
481 basics.timestamp = from.timestamp;
482 basics.lastref = from.lastref;
483 basics.expires = from.expires;
484 basics.lastmod = from.lastmod;
485 basics.swap_file_sz = from.swap_file_sz;
486 basics.refcount = from.refcount;
487 basics.flags = from.flags;
488}
489
50dc81ec
AR
490void
491Ipc::StoreMapAnchor::rewind()
492{
ce49546e 493 assert(writing());
50dc81ec
AR
494 start = 0;
495 memset(&key, 0, sizeof(key));
496 memset(&basics, 0, sizeof(basics));
497 // but keep the lock
498}
499
1860fbac
AR
500Ipc::StoreMap::Owner::Owner(): anchors(NULL), slices(NULL)
501{
502}
503
504Ipc::StoreMap::Owner::~Owner()
505{
506 delete anchors;
507 delete slices;
508}
509
510/* Ipc::StoreMapAnchors */
44c95fcf 511
1860fbac
AR
512Ipc::StoreMapAnchors::StoreMapAnchors(const int aCapacity):
513 count(0),
36c84e19 514 victim(0),
1860fbac
AR
515 capacity(aCapacity),
516 items(aCapacity)
68353d5a
DK
517{
518}
519
520size_t
1860fbac 521Ipc::StoreMapAnchors::sharedMemorySize() const
44c95fcf 522{
1860fbac 523 return SharedMemorySize(capacity);
44c95fcf
AR
524}
525
526size_t
1860fbac 527Ipc::StoreMapAnchors::SharedMemorySize(const int capacity)
44c95fcf 528{
1860fbac 529 return sizeof(StoreMapAnchors) + capacity * sizeof(StoreMapAnchor);
44c95fcf
AR
530}
531