]> git.ipfire.org Git - thirdparty/squid.git/blame - src/Transients.cc
Do not leak Security::CertErrors created in X509_verify_cert() (#1346)
[thirdparty/squid.git] / src / Transients.cc
CommitLineData
9a9954ba 1/*
b8ae064d 2 * Copyright (C) 1996-2023 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
aee3523a 30Transients::Transients(): map(nullptr), locals(nullptr)
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
aee3523a 52 locals = new Locals(entryLimit, nullptr);
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();
8b082ed9
FC
67#else
68 (void)stats;
9a9954ba
AR
69#endif
70}
71
72void
73Transients::stat(StoreEntry &e) const
74{
75 storeAppendPrintf(&e, "\n\nTransient Objects\n");
76
77 storeAppendPrintf(&e, "Maximum Size: %.0f KB\n", maxSize()/1024.0);
78 storeAppendPrintf(&e, "Current Size: %.2f KB %.2f%%\n",
79 currentSize() / 1024.0,
80 Math::doublePercent(currentSize(), maxSize()));
81
82 if (map) {
83 const int limit = map->entryLimit();
84 storeAppendPrintf(&e, "Maximum entries: %9d\n", limit);
85 if (limit > 0) {
86 storeAppendPrintf(&e, "Current entries: %" PRId64 " %.2f%%\n",
87 currentCount(), (100.0 * currentCount() / limit));
88 }
89 }
90}
91
92void
93Transients::maintain()
94{
e4d13993 95 // no lazy garbage collection needed
9a9954ba
AR
96}
97
98uint64_t
99Transients::minSize() const
100{
101 return 0; // XXX: irrelevant, but Store parent forces us to implement this
102}
103
104uint64_t
105Transients::maxSize() const
106{
107 // Squid currently does not limit the total size of all transient objects
108 return std::numeric_limits<uint64_t>::max();
109}
110
111uint64_t
112Transients::currentSize() const
113{
114 // TODO: we do not get enough information to calculate this
115 // StoreEntry should update associated stores when its size changes
116 return 0;
117}
118
119uint64_t
120Transients::currentCount() const
121{
122 return map ? map->entryCount() : 0;
123}
124
125int64_t
126Transients::maxObjectSize() const
127{
128 // Squid currently does not limit the size of a transient object
129 return std::numeric_limits<uint64_t>::max();
130}
131
132void
133Transients::reference(StoreEntry &)
134{
e4d13993 135 // no replacement policy (but the cache(s) storing the entry may have one)
9a9954ba
AR
136}
137
138bool
2745fea5 139Transients::dereference(StoreEntry &)
9a9954ba
AR
140{
141 // no need to keep e in the global store_table for us; we have our own map
142 return false;
143}
144
9a9954ba
AR
145StoreEntry *
146Transients::get(const cache_key *key)
147{
148 if (!map)
aee3523a 149 return nullptr;
9a9954ba
AR
150
151 sfileno index;
1bfe9ade
AR
152 const Ipc::StoreMapAnchor *anchor = map->openForReading(key, index);
153 if (!anchor)
aee3523a 154 return nullptr;
9a9954ba 155
6919be24
AR
156 // If we already have a local entry, the store_table should have found it.
157 // Since it did not, the local entry key must have changed from public to
158 // private. We still need to keep the private entry around for syncing as
159 // its clients depend on it, but we should not allow new clients to join.
160 if (StoreEntry *oldE = locals->at(index)) {
161 debugs(20, 3, "not joining private " << *oldE);
162 assert(EBIT_TEST(oldE->flags, KEY_PRIVATE));
d1d3b4dc 163 map->closeForReadingAndFreeIdle(index);
4310f8b0 164 return nullptr;
1bfe9ade 165 }
4475555f 166
4310f8b0
EB
167 StoreEntry *e = new StoreEntry();
168 e->createMemObject();
5bd8ce13 169 anchorEntry(*e, index, *anchor);
6c8cbe63 170
4310f8b0 171 // keep read lock to receive updates from others
9a9954ba
AR
172 return e;
173}
174
6919be24
AR
175StoreEntry *
176Transients::findCollapsed(const sfileno index)
177{
178 if (!map)
aee3523a 179 return nullptr;
6919be24
AR
180
181 if (StoreEntry *oldE = locals->at(index)) {
182 debugs(20, 5, "found " << *oldE << " at " << index << " in " << MapLabel);
183 assert(oldE->mem_obj && oldE->mem_obj->xitTable.index == index);
184 return oldE;
185 }
186
187 debugs(20, 3, "no entry at " << index << " in " << MapLabel);
aee3523a 188 return nullptr;
6919be24
AR
189}
190
9a9954ba 191void
4310f8b0 192Transients::monitorIo(StoreEntry *e, const cache_key *key, const Store::IoStatus direction)
9a9954ba 193{
4310f8b0
EB
194 if (!e->hasTransients()) {
195 addEntry(e, key, direction);
8253d451 196 assert(e->hasTransients());
9d4e9cfb 197 }
9a9954ba 198
4310f8b0
EB
199 const auto index = e->mem_obj->xitTable.index;
200 if (const auto old = locals->at(index)) {
201 assert(old == e);
202 } else {
203 // We do not lock e because we do not want to prevent its destruction;
204 // e is tied to us via mem_obj so we will know when it is destructed.
205 locals->at(index) = e;
9d4e9cfb 206 }
9a9954ba
AR
207}
208
8253d451 209/// creates a new Transients entry
4310f8b0
EB
210void
211Transients::addEntry(StoreEntry *e, const cache_key *key, const Store::IoStatus direction)
9a9954ba 212{
4310f8b0
EB
213 assert(e);
214 assert(e->mem_obj);
215 assert(!e->hasTransients());
9a9954ba 216
4310f8b0 217 Must(map); // configured to track transients
9a9954ba 218
5bd8ce13
AR
219 if (direction == Store::ioWriting)
220 return addWriterEntry(*e, key);
221
222 assert(direction == Store::ioReading);
223 addReaderEntry(*e, key);
224}
225
226/// addEntry() helper used for cache entry creators/writers
227void
228Transients::addWriterEntry(StoreEntry &e, const cache_key *key)
229{
4310f8b0 230 sfileno index = 0;
5bd8ce13
AR
231 const auto anchor = map->openForWriting(key, index);
232 if (!anchor)
233 throw TextException("writer collision", Here());
9a9954ba 234
8253d451 235 // set ASAP in hope to unlock the slot if something throws
5bd8ce13
AR
236 // and to provide index to such methods as hasWriter()
237 auto &xitTable = e.mem_obj->xitTable;
238 xitTable.index = index;
239 xitTable.io = Store::ioWriting;
240
241 anchor->set(e, key);
242 // allow reading and receive remote DELETE events, but do not switch to
243 // the reading lock because transientReaders() callers want true readers
244 map->startAppending(index);
245}
246
247/// addEntry() helper used for cache readers
248/// readers do not modify the cache, but they must create a Transients entry
249void
250Transients::addReaderEntry(StoreEntry &e, const cache_key *key)
251{
252 sfileno index = 0;
253 const auto anchor = map->openOrCreateForReading(key, index, e);
254 if (!anchor)
255 throw TextException("reader collision", Here());
256
257 anchorEntry(e, index, *anchor);
258 // keep the entry locked (for reading) to receive remote DELETE events
259}
260
261/// fills (recently created) StoreEntry with information currently in Transients
262void
263Transients::anchorEntry(StoreEntry &e, const sfileno index, const Ipc::StoreMapAnchor &anchor)
264{
265 // set ASAP in hope to unlock the slot if something throws
266 // and to provide index to such methods as hasWriter()
267 auto &xitTable = e.mem_obj->xitTable;
268 xitTable.index = index;
269 xitTable.io = Store::ioReading;
270
5bd8ce13 271 anchor.exportInto(e);
9a9954ba
AR
272}
273
d1d3b4dc
EB
274bool
275Transients::hasWriter(const StoreEntry &e)
276{
277 if (!e.hasTransients())
278 return false;
279 return map->peekAtWriter(e.mem_obj->xitTable.index);
280}
281
9a9954ba 282void
ced8def3 283Transients::noteFreeMapSlice(const Ipc::StoreMapSliceId)
9a9954ba
AR
284{
285 // TODO: we should probably find the entry being deleted and abort it
286}
287
4475555f 288void
d2a6dcba 289Transients::status(const StoreEntry &entry, Transients::EntryStatus &entryStatus) const
4475555f
AR
290{
291 assert(map);
4310f8b0
EB
292 assert(entry.hasTransients());
293 const auto idx = entry.mem_obj->xitTable.index;
294 const auto &anchor = isWriter(entry) ?
295 map->writeableEntry(idx) : map->readableEntry(idx);
24c93780 296 entryStatus.hasWriter = anchor.writing();
d2a6dcba 297 entryStatus.waitingToBeFreed = anchor.waitingToBeFreed;
4475555f
AR
298}
299
99921d9d
AR
300void
301Transients::completeWriting(const StoreEntry &e)
302{
24c93780 303 debugs(20, 5, e);
4310f8b0
EB
304 assert(e.hasTransients());
305 assert(isWriter(e));
8253d451 306 map->switchWritingToReading(e.mem_obj->xitTable.index);
4310f8b0 307 e.mem_obj->xitTable.io = Store::ioReading;
24c93780 308 CollapsedForwarding::Broadcast(e);
99921d9d
AR
309}
310
d366a7fa
AR
311int
312Transients::readers(const StoreEntry &e) const
313{
4310f8b0 314 if (e.hasTransients()) {
d366a7fa
AR
315 assert(map);
316 return map->peekAtEntry(e.mem_obj->xitTable.index).lock.readers;
317 }
318 return 0;
319}
320
1bfe9ade 321void
4310f8b0
EB
322Transients::evictCached(StoreEntry &e)
323{
324 debugs(20, 5, e);
325 if (e.hasTransients()) {
326 const auto index = e.mem_obj->xitTable.index;
327 if (map->freeEntry(index)) {
328 // Delay syncCollapsed(index) which may end `e` wait for updates.
329 // Calling it directly/here creates complex reentrant call chains.
330 CollapsedForwarding::Broadcast(e, true);
331 }
332 } // else nothing to do because e must be private
2745fea5
AR
333}
334
335void
4310f8b0 336Transients::evictIfFound(const cache_key *key)
1bfe9ade 337{
4310f8b0
EB
338 if (!map)
339 return;
340
341 const sfileno index = map->fileNoByKey(key);
342 if (map->freeEntry(index))
343 CollapsedForwarding::Broadcast(index, true);
1bfe9ade
AR
344}
345
4475555f 346void
4310f8b0 347Transients::disconnect(StoreEntry &entry)
4475555f 348{
4310f8b0
EB
349 debugs(20, 5, entry);
350 if (entry.hasTransients()) {
351 auto &xitTable = entry.mem_obj->xitTable;
99921d9d 352 assert(map);
4310f8b0 353 if (isWriter(entry)) {
24c93780
AR
354 // completeWriting() was not called, so there could be an active
355 // Store writer out there, but we should not abortWriting() here
356 // because another writer may have succeeded, making readers happy.
357 // If none succeeded, the readers will notice the lack of writers.
358 map->closeForWriting(xitTable.index);
359 CollapsedForwarding::Broadcast(entry);
99921d9d 360 } else {
4310f8b0 361 assert(isReader(entry));
d1d3b4dc 362 map->closeForReadingAndFreeIdle(xitTable.index);
99921d9d 363 }
4310f8b0
EB
364 locals->at(xitTable.index) = nullptr;
365 xitTable.index = -1;
366 xitTable.io = Store::ioDone;
99921d9d 367 }
4475555f
AR
368}
369
9a9954ba
AR
370/// calculates maximum number of entries we need to store and map
371int64_t
372Transients::EntryLimit()
373{
daed75a9
EB
374 return (UsingSmp() && Store::Controller::SmpAware()) ?
375 Config.shared_transient_entries_limit : 0;
9a9954ba
AR
376}
377
4310f8b0
EB
378bool
379Transients::markedForDeletion(const cache_key *key) const
380{
381 assert(map);
382 return map->markedForDeletion(key);
383}
384
385bool
386Transients::isReader(const StoreEntry &e) const
387{
388 return e.mem_obj && e.mem_obj->xitTable.io == Store::ioReading;
389}
390
391bool
392Transients::isWriter(const StoreEntry &e) const
393{
394 return e.mem_obj && e.mem_obj->xitTable.io == Store::ioWriting;
395}
396
9a9954ba
AR
397/// initializes shared memory segment used by Transients
398class TransientsRr: public Ipc::Mem::RegisteredRunner
399{
400public:
401 /* RegisteredRunner API */
337b9aa4
AR
402 void useConfig() override;
403 ~TransientsRr() override;
9a9954ba
AR
404
405protected:
337b9aa4 406 void create() override;
9a9954ba
AR
407
408private:
4310f8b0 409 TransientsMap::Owner *mapOwner = nullptr;
9a9954ba
AR
410};
411
21b7990f 412RunnerRegistrationEntry(TransientsRr);
9a9954ba 413
e4d13993 414void
21b7990f 415TransientsRr::useConfig()
9a9954ba
AR
416{
417 assert(Config.memShared.configured());
21b7990f 418 Ipc::Mem::RegisteredRunner::useConfig();
9a9954ba
AR
419}
420
e4d13993 421void
21b7990f 422TransientsRr::create()
9a9954ba 423{
9a9954ba 424 const int64_t entryLimit = Transients::EntryLimit();
9a9954ba
AR
425 if (entryLimit <= 0)
426 return; // no SMP configured or a misconfiguration
427
428 Must(!mapOwner);
429 mapOwner = TransientsMap::Init(MapLabel, entryLimit);
430}
431
432TransientsRr::~TransientsRr()
433{
434 delete mapOwner;
435}
f53969cc 436