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