]> git.ipfire.org Git - thirdparty/squid.git/blame - src/Transients.cc
Support "file" syntax for 'squid_error' and 'has' ACL parameters (#874)
[thirdparty/squid.git] / src / Transients.cc
CommitLineData
9a9954ba 1/*
f70aedc4 2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
9a9954ba 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.
9a9954ba
AR
7 */
8
bbc27441
AJ
9/* DEBUG: section 20 Storage Manager */
10
9a9954ba
AR
11#include "squid.h"
12#include "base/RunnersRegistry.h"
e4d13993 13#include "CollapsedForwarding.h"
9a9954ba
AR
14#include "HttpReply.h"
15#include "ipc/mem/Page.h"
16#include "ipc/mem/Pages.h"
17#include "MemObject.h"
9a9954ba
AR
18#include "mime_header.h"
19#include "SquidConfig.h"
20#include "SquidMath.h"
21#include "StoreStats.h"
22#include "tools.h"
e4d13993 23#include "Transients.h"
9a9954ba 24
9a9954ba 25#include <limits>
9a9954ba 26
1860fbac
AR
27/// shared memory segment path to use for Transients map
28static const SBuf MapLabel("transients_map");
9a9954ba 29
6919be24 30Transients::Transients(): map(NULL), locals(NULL)
9a9954ba 31{
9a9954ba
AR
32}
33
34Transients::~Transients()
35{
36 delete map;
6919be24 37 delete locals;
9a9954ba
AR
38}
39
40void
41Transients::init()
42{
daed75a9 43 assert(Enabled());
9a9954ba 44 const int64_t entryLimit = EntryLimit();
daed75a9 45 assert(entryLimit > 0);
9a9954ba
AR
46
47 Must(!map);
48 map = new TransientsMap(MapLabel);
49 map->cleaner = this;
b2aca62a 50 map->disableHitValidation(); // Transients lacks slices to validate
6919be24 51
8bcca0f8 52 locals = new Locals(entryLimit, 0);
9a9954ba
AR
53}
54
55void
56Transients::getStats(StoreInfoStats &stats) const
57{
58#if TRANSIENT_STATS_SUPPORTED
59 const size_t pageSize = Ipc::Mem::PageSize();
60
61 stats.mem.shared = true;
62 stats.mem.capacity =
63 Ipc::Mem::PageLimit(Ipc::Mem::PageId::cachePage) * pageSize;
64 stats.mem.size =
65 Ipc::Mem::PageLevel(Ipc::Mem::PageId::cachePage) * pageSize;
66 stats.mem.count = currentCount();
67#endif
68}
69
70void
71Transients::stat(StoreEntry &e) const
72{
73 storeAppendPrintf(&e, "\n\nTransient Objects\n");
74
75 storeAppendPrintf(&e, "Maximum Size: %.0f KB\n", maxSize()/1024.0);
76 storeAppendPrintf(&e, "Current Size: %.2f KB %.2f%%\n",
77 currentSize() / 1024.0,
78 Math::doublePercent(currentSize(), maxSize()));
79
80 if (map) {
81 const int limit = map->entryLimit();
82 storeAppendPrintf(&e, "Maximum entries: %9d\n", limit);
83 if (limit > 0) {
84 storeAppendPrintf(&e, "Current entries: %" PRId64 " %.2f%%\n",
85 currentCount(), (100.0 * currentCount() / limit));
86 }
87 }
88}
89
90void
91Transients::maintain()
92{
e4d13993 93 // no lazy garbage collection needed
9a9954ba
AR
94}
95
96uint64_t
97Transients::minSize() const
98{
99 return 0; // XXX: irrelevant, but Store parent forces us to implement this
100}
101
102uint64_t
103Transients::maxSize() const
104{
105 // Squid currently does not limit the total size of all transient objects
106 return std::numeric_limits<uint64_t>::max();
107}
108
109uint64_t
110Transients::currentSize() const
111{
112 // TODO: we do not get enough information to calculate this
113 // StoreEntry should update associated stores when its size changes
114 return 0;
115}
116
117uint64_t
118Transients::currentCount() const
119{
120 return map ? map->entryCount() : 0;
121}
122
123int64_t
124Transients::maxObjectSize() const
125{
126 // Squid currently does not limit the size of a transient object
127 return std::numeric_limits<uint64_t>::max();
128}
129
130void
131Transients::reference(StoreEntry &)
132{
e4d13993 133 // no replacement policy (but the cache(s) storing the entry may have one)
9a9954ba
AR
134}
135
136bool
2745fea5 137Transients::dereference(StoreEntry &)
9a9954ba
AR
138{
139 // no need to keep e in the global store_table for us; we have our own map
140 return false;
141}
142
9a9954ba
AR
143StoreEntry *
144Transients::get(const cache_key *key)
145{
146 if (!map)
147 return NULL;
148
149 sfileno index;
1bfe9ade
AR
150 const Ipc::StoreMapAnchor *anchor = map->openForReading(key, index);
151 if (!anchor)
9a9954ba
AR
152 return NULL;
153
6919be24
AR
154 // If we already have a local entry, the store_table should have found it.
155 // Since it did not, the local entry key must have changed from public to
156 // private. We still need to keep the private entry around for syncing as
157 // its clients depend on it, but we should not allow new clients to join.
158 if (StoreEntry *oldE = locals->at(index)) {
159 debugs(20, 3, "not joining private " << *oldE);
160 assert(EBIT_TEST(oldE->flags, KEY_PRIVATE));
d1d3b4dc 161 map->closeForReadingAndFreeIdle(index);
4310f8b0 162 return nullptr;
1bfe9ade 163 }
4475555f 164
6c8cbe63
D
165 // store hadWriter before checking ENTRY_REQUIRES_COLLAPSING to avoid racing
166 // the writer that clears that flag and then leaves
167 const auto hadWriter = map->peekAtWriter(index);
168 if (!hadWriter && EBIT_TEST(anchor->basics.flags, ENTRY_REQUIRES_COLLAPSING)) {
169 debugs(20, 3, "not joining abandoned entry " << index);
170 map->closeForReadingAndFreeIdle(index);
171 return nullptr;
172 }
173
4310f8b0
EB
174 StoreEntry *e = new StoreEntry();
175 e->createMemObject();
5bd8ce13 176 anchorEntry(*e, index, *anchor);
6c8cbe63 177
4310f8b0 178 // keep read lock to receive updates from others
9a9954ba
AR
179 return e;
180}
181
6919be24
AR
182StoreEntry *
183Transients::findCollapsed(const sfileno index)
184{
185 if (!map)
186 return NULL;
187
188 if (StoreEntry *oldE = locals->at(index)) {
189 debugs(20, 5, "found " << *oldE << " at " << index << " in " << MapLabel);
190 assert(oldE->mem_obj && oldE->mem_obj->xitTable.index == index);
191 return oldE;
192 }
193
194 debugs(20, 3, "no entry at " << index << " in " << MapLabel);
195 return NULL;
196}
197
d2a6dcba
EB
198void
199Transients::clearCollapsingRequirement(const StoreEntry &e)
200{
201 assert(map);
202 assert(e.hasTransients());
203 assert(isWriter(e));
204 const auto idx = e.mem_obj->xitTable.index;
205 auto &anchor = map->writeableEntry(idx);
206 if (EBIT_TEST(anchor.basics.flags, ENTRY_REQUIRES_COLLAPSING)) {
207 EBIT_CLR(anchor.basics.flags, ENTRY_REQUIRES_COLLAPSING);
208 CollapsedForwarding::Broadcast(e);
209 }
210}
211
9a9954ba 212void
4310f8b0 213Transients::monitorIo(StoreEntry *e, const cache_key *key, const Store::IoStatus direction)
9a9954ba 214{
4310f8b0
EB
215 if (!e->hasTransients()) {
216 addEntry(e, key, direction);
8253d451 217 assert(e->hasTransients());
9d4e9cfb 218 }
9a9954ba 219
4310f8b0
EB
220 const auto index = e->mem_obj->xitTable.index;
221 if (const auto old = locals->at(index)) {
222 assert(old == e);
223 } else {
224 // We do not lock e because we do not want to prevent its destruction;
225 // e is tied to us via mem_obj so we will know when it is destructed.
226 locals->at(index) = e;
9d4e9cfb 227 }
9a9954ba
AR
228}
229
8253d451 230/// creates a new Transients entry
4310f8b0
EB
231void
232Transients::addEntry(StoreEntry *e, const cache_key *key, const Store::IoStatus direction)
9a9954ba 233{
4310f8b0
EB
234 assert(e);
235 assert(e->mem_obj);
236 assert(!e->hasTransients());
9a9954ba 237
4310f8b0 238 Must(map); // configured to track transients
9a9954ba 239
5bd8ce13
AR
240 if (direction == Store::ioWriting)
241 return addWriterEntry(*e, key);
242
243 assert(direction == Store::ioReading);
244 addReaderEntry(*e, key);
245}
246
247/// addEntry() helper used for cache entry creators/writers
248void
249Transients::addWriterEntry(StoreEntry &e, const cache_key *key)
250{
4310f8b0 251 sfileno index = 0;
5bd8ce13
AR
252 const auto anchor = map->openForWriting(key, index);
253 if (!anchor)
254 throw TextException("writer collision", Here());
9a9954ba 255
8253d451 256 // set ASAP in hope to unlock the slot if something throws
5bd8ce13
AR
257 // and to provide index to such methods as hasWriter()
258 auto &xitTable = e.mem_obj->xitTable;
259 xitTable.index = index;
260 xitTable.io = Store::ioWriting;
261
262 anchor->set(e, key);
263 // allow reading and receive remote DELETE events, but do not switch to
264 // the reading lock because transientReaders() callers want true readers
265 map->startAppending(index);
266}
267
268/// addEntry() helper used for cache readers
269/// readers do not modify the cache, but they must create a Transients entry
270void
271Transients::addReaderEntry(StoreEntry &e, const cache_key *key)
272{
273 sfileno index = 0;
274 const auto anchor = map->openOrCreateForReading(key, index, e);
275 if (!anchor)
276 throw TextException("reader collision", Here());
277
278 anchorEntry(e, index, *anchor);
279 // keep the entry locked (for reading) to receive remote DELETE events
280}
281
282/// fills (recently created) StoreEntry with information currently in Transients
283void
284Transients::anchorEntry(StoreEntry &e, const sfileno index, const Ipc::StoreMapAnchor &anchor)
285{
286 // set ASAP in hope to unlock the slot if something throws
287 // and to provide index to such methods as hasWriter()
288 auto &xitTable = e.mem_obj->xitTable;
289 xitTable.index = index;
290 xitTable.io = Store::ioReading;
291
292 const auto hadWriter = hasWriter(e); // before computing collapsingRequired
293 anchor.exportInto(e);
294 const bool collapsingRequired = EBIT_TEST(anchor.basics.flags, ENTRY_REQUIRES_COLLAPSING);
295 assert(!collapsingRequired || hadWriter);
296 e.setCollapsingRequirement(collapsingRequired);
9a9954ba
AR
297}
298
d1d3b4dc
EB
299bool
300Transients::hasWriter(const StoreEntry &e)
301{
302 if (!e.hasTransients())
303 return false;
304 return map->peekAtWriter(e.mem_obj->xitTable.index);
305}
306
9a9954ba 307void
ced8def3 308Transients::noteFreeMapSlice(const Ipc::StoreMapSliceId)
9a9954ba
AR
309{
310 // TODO: we should probably find the entry being deleted and abort it
311}
312
4475555f 313void
d2a6dcba 314Transients::status(const StoreEntry &entry, Transients::EntryStatus &entryStatus) const
4475555f
AR
315{
316 assert(map);
4310f8b0
EB
317 assert(entry.hasTransients());
318 const auto idx = entry.mem_obj->xitTable.index;
319 const auto &anchor = isWriter(entry) ?
320 map->writeableEntry(idx) : map->readableEntry(idx);
d2a6dcba
EB
321 entryStatus.abortedByWriter = anchor.writerHalted;
322 entryStatus.waitingToBeFreed = anchor.waitingToBeFreed;
323 entryStatus.collapsed = EBIT_TEST(anchor.basics.flags, ENTRY_REQUIRES_COLLAPSING);
4475555f
AR
324}
325
99921d9d
AR
326void
327Transients::completeWriting(const StoreEntry &e)
328{
4310f8b0
EB
329 assert(e.hasTransients());
330 assert(isWriter(e));
8253d451 331 map->switchWritingToReading(e.mem_obj->xitTable.index);
4310f8b0 332 e.mem_obj->xitTable.io = Store::ioReading;
99921d9d
AR
333}
334
d366a7fa
AR
335int
336Transients::readers(const StoreEntry &e) const
337{
4310f8b0 338 if (e.hasTransients()) {
d366a7fa
AR
339 assert(map);
340 return map->peekAtEntry(e.mem_obj->xitTable.index).lock.readers;
341 }
342 return 0;
343}
344
1bfe9ade 345void
4310f8b0
EB
346Transients::evictCached(StoreEntry &e)
347{
348 debugs(20, 5, e);
349 if (e.hasTransients()) {
350 const auto index = e.mem_obj->xitTable.index;
351 if (map->freeEntry(index)) {
352 // Delay syncCollapsed(index) which may end `e` wait for updates.
353 // Calling it directly/here creates complex reentrant call chains.
354 CollapsedForwarding::Broadcast(e, true);
355 }
356 } // else nothing to do because e must be private
2745fea5
AR
357}
358
359void
4310f8b0 360Transients::evictIfFound(const cache_key *key)
1bfe9ade 361{
4310f8b0
EB
362 if (!map)
363 return;
364
365 const sfileno index = map->fileNoByKey(key);
366 if (map->freeEntry(index))
367 CollapsedForwarding::Broadcast(index, true);
1bfe9ade
AR
368}
369
4475555f 370void
4310f8b0 371Transients::disconnect(StoreEntry &entry)
4475555f 372{
4310f8b0
EB
373 debugs(20, 5, entry);
374 if (entry.hasTransients()) {
375 auto &xitTable = entry.mem_obj->xitTable;
99921d9d 376 assert(map);
4310f8b0
EB
377 if (isWriter(entry)) {
378 map->abortWriting(xitTable.index);
99921d9d 379 } else {
4310f8b0 380 assert(isReader(entry));
d1d3b4dc 381 map->closeForReadingAndFreeIdle(xitTable.index);
99921d9d 382 }
4310f8b0
EB
383 locals->at(xitTable.index) = nullptr;
384 xitTable.index = -1;
385 xitTable.io = Store::ioDone;
99921d9d 386 }
4475555f
AR
387}
388
9a9954ba
AR
389/// calculates maximum number of entries we need to store and map
390int64_t
391Transients::EntryLimit()
392{
daed75a9
EB
393 return (UsingSmp() && Store::Controller::SmpAware()) ?
394 Config.shared_transient_entries_limit : 0;
9a9954ba
AR
395}
396
4310f8b0
EB
397bool
398Transients::markedForDeletion(const cache_key *key) const
399{
400 assert(map);
401 return map->markedForDeletion(key);
402}
403
404bool
405Transients::isReader(const StoreEntry &e) const
406{
407 return e.mem_obj && e.mem_obj->xitTable.io == Store::ioReading;
408}
409
410bool
411Transients::isWriter(const StoreEntry &e) const
412{
413 return e.mem_obj && e.mem_obj->xitTable.io == Store::ioWriting;
414}
415
9a9954ba
AR
416/// initializes shared memory segment used by Transients
417class TransientsRr: public Ipc::Mem::RegisteredRunner
418{
419public:
420 /* RegisteredRunner API */
21b7990f 421 virtual void useConfig();
9a9954ba
AR
422 virtual ~TransientsRr();
423
424protected:
21b7990f 425 virtual void create();
9a9954ba
AR
426
427private:
4310f8b0 428 TransientsMap::Owner *mapOwner = nullptr;
9a9954ba
AR
429};
430
21b7990f 431RunnerRegistrationEntry(TransientsRr);
9a9954ba 432
e4d13993 433void
21b7990f 434TransientsRr::useConfig()
9a9954ba
AR
435{
436 assert(Config.memShared.configured());
21b7990f 437 Ipc::Mem::RegisteredRunner::useConfig();
9a9954ba
AR
438}
439
e4d13993 440void
21b7990f 441TransientsRr::create()
9a9954ba 442{
9a9954ba 443 const int64_t entryLimit = Transients::EntryLimit();
9a9954ba
AR
444 if (entryLimit <= 0)
445 return; // no SMP configured or a misconfiguration
446
447 Must(!mapOwner);
448 mapOwner = TransientsMap::Init(MapLabel, entryLimit);
449}
450
451TransientsRr::~TransientsRr()
452{
453 delete mapOwner;
454}
f53969cc 455