]> git.ipfire.org Git - thirdparty/squid.git/blame - src/MemStore.cc
Renamed squid.h to squid-old.h and config.h to squid.h
[thirdparty/squid.git] / src / MemStore.cc
CommitLineData
9487bae9
AR
1/*
2 * $Id$
3 *
4 * DEBUG: section 20 Memory Cache
5 *
6 */
7
f7f3304a 8#include "squid.h"
a4555399 9#include "base/RunnersRegistry.h"
9487bae9
AR
10#include "ipc/mem/Page.h"
11#include "ipc/mem/Pages.h"
12#include "MemObject.h"
13#include "MemStore.h"
93bc1434 14#include "StoreStats.h"
9487bae9
AR
15#include "HttpReply.h"
16
a4555399
AR
17/// shared memory segment path to use for MemStore maps
18static const char *ShmLabel = "cache_mem";
9487bae9
AR
19
20// XXX: support storage using more than one page per entry
21
17cf0a47 22MemStore::MemStore(): map(NULL), theCurrentSize(0)
9487bae9
AR
23{
24}
25
26MemStore::~MemStore()
27{
28 delete map;
29}
30
31void
9199139f
AR
32MemStore::init()
33{
a4555399
AR
34 const int64_t entryLimit = EntryLimit();
35 if (entryLimit <= 0)
36 return; // no memory cache configured or a misconfiguration
37
ed5d80d3
AR
38 const int64_t diskMaxSize = Store::Root().maxObjectSize();
39 const int64_t memMaxSize = maxObjectSize();
40 if (diskMaxSize == -1) {
41 debugs(20, DBG_IMPORTANT, "WARNING: disk-cache maximum object size "
42 "is unlimited but mem-cache maximum object size is " <<
43 memMaxSize / 1024.0 << " KB");
44 } else if (diskMaxSize > memMaxSize) {
45 debugs(20, DBG_IMPORTANT, "WARNING: disk-cache maximum object size "
46 "is too large for mem-cache: " <<
47 diskMaxSize / 1024.0 << " KB > " <<
48 memMaxSize / 1024.0 << " KB");
af2fda07
DK
49 }
50
a4555399
AR
51 map = new MemStoreMap(ShmLabel);
52 map->cleaner = this;
9487bae9
AR
53}
54
93bc1434
AR
55void
56MemStore::getStats(StoreInfoStats &stats) const
57{
58 const size_t pageSize = Ipc::Mem::PageSize();
59
60 stats.mem.shared = true;
61 stats.mem.capacity =
62 Ipc::Mem::PageLimit(Ipc::Mem::PageId::cachePage) * pageSize;
63 stats.mem.size =
64 Ipc::Mem::PageLevel(Ipc::Mem::PageId::cachePage) * pageSize;
65 stats.mem.count = currentCount();
66}
67
9487bae9 68void
c4e688b7 69MemStore::stat(StoreEntry &e) const
9487bae9 70{
c4e688b7
AR
71 storeAppendPrintf(&e, "\n\nShared Memory Cache\n");
72
73 storeAppendPrintf(&e, "Maximum Size: %.0f KB\n", Config.memMaxSize/1024.0);
74
75 if (map) {
76 const int limit = map->entryLimit();
77 storeAppendPrintf(&e, "Maximum entries: %9d\n", limit);
78 if (limit > 0) {
39c1e1d9 79 storeAppendPrintf(&e, "Current entries: %"PRId64" %.2f%%\n",
9199139f 80 currentCount(), (100.0 * currentCount() / limit));
c4e688b7
AR
81
82 if (limit < 100) { // XXX: otherwise too expensive to count
83 Ipc::ReadWriteLockStats stats;
84 map->updateStats(stats);
85 stats.dump(e);
9199139f
AR
86 }
87 }
88 }
9487bae9
AR
89}
90
91void
92MemStore::maintain()
93{
94}
95
96uint64_t
97MemStore::minSize() const
98{
99 return 0; // XXX: irrelevant, but Store parent forces us to implement this
100}
101
102uint64_t
103MemStore::maxSize() const
104{
105 return 0; // XXX: make configurable
106}
107
39c1e1d9
DK
108uint64_t
109MemStore::currentSize() const
110{
57f583f1 111 return theCurrentSize;
39c1e1d9
DK
112}
113
114uint64_t
115MemStore::currentCount() const
116{
117 return map ? map->entryCount() : 0;
118}
119
af2fda07
DK
120int64_t
121MemStore::maxObjectSize() const
122{
123 return Ipc::Mem::PageSize();
124}
125
9487bae9
AR
126void
127MemStore::reference(StoreEntry &)
128{
129}
130
4c973beb 131bool
9487bae9
AR
132MemStore::dereference(StoreEntry &)
133{
4c973beb
AR
134 // no need to keep e in the global store_table for us; we have our own map
135 return false;
9487bae9
AR
136}
137
138int
139MemStore::callback()
140{
141 return 0;
142}
143
144StoreSearch *
145MemStore::search(String const, HttpRequest *)
146{
147 fatal("not implemented");
148 return NULL;
149}
150
151StoreEntry *
152MemStore::get(const cache_key *key)
153{
154 if (!map)
155 return NULL;
156
157 // XXX: replace sfileno with a bigger word (sfileno is only for cache_dirs)
158 sfileno index;
159 const Ipc::StoreMapSlot *const slot = map->openForReading(key, index);
160 if (!slot)
161 return NULL;
162
163 const Ipc::StoreMapSlot::Basics &basics = slot->basics;
164 const MemStoreMap::Extras &extras = map->extras(index);
165
166 // create a brand new store entry and initialize it with stored info
167 StoreEntry *e = new StoreEntry();
168 e->lock_count = 0;
169
170 e->swap_file_sz = basics.swap_file_sz;
171 e->lastref = basics.lastref;
172 e->timestamp = basics.timestamp;
173 e->expires = basics.expires;
174 e->lastmod = basics.lastmod;
175 e->refcount = basics.refcount;
176 e->flags = basics.flags;
177
178 e->store_status = STORE_OK;
179 e->mem_status = IN_MEMORY; // setMemStatus(IN_MEMORY) requires mem_obj
180 //e->swap_status = set in StoreEntry constructor to SWAPOUT_NONE;
181 e->ping_status = PING_NONE;
182
183 EBIT_SET(e->flags, ENTRY_CACHABLE);
184 EBIT_CLR(e->flags, RELEASE_REQUEST);
185 EBIT_CLR(e->flags, KEY_PRIVATE);
186 EBIT_SET(e->flags, ENTRY_VALIDATED);
187
188 const bool copied = copyFromShm(*e, extras);
189
190 // we copied everything we could to local memory; no more need to lock
191 map->closeForReading(index);
192
193 if (copied) {
194 e->hashInsert(key);
195 return e;
196 }
197
198 debugs(20, 3, HERE << "mem-loading failed; freeing " << index);
199 map->free(index); // do not let others into the same trap
200 return NULL;
201}
202
203void
204MemStore::get(String const key, STOREGETCLIENT aCallback, void *aCallbackData)
205{
206 // XXX: not needed but Store parent forces us to implement this
207 fatal("MemStore::get(key,callback,data) should not be called");
208}
209
210bool
211MemStore::copyFromShm(StoreEntry &e, const MemStoreMap::Extras &extras)
212{
213 const Ipc::Mem::PageId &page = extras.page;
214
215 StoreIOBuffer sourceBuf(extras.storedSize, 0,
9199139f 216 static_cast<char*>(PagePointer(page)));
9487bae9
AR
217
218 // XXX: We do not know the URLs yet, only the key, but we need to parse and
219 // store the response for the Root().get() callers to be happy because they
220 // expect IN_MEMORY entries to already have the response headers and body.
221 // At least one caller calls createMemObject() if there is not one, so
222 // we hide the true object until that happens (to avoid leaking TBD URLs).
223 e.createMemObject("TBD", "TBD");
224
225 // emulate the usual Store code but w/o inapplicable checks and callbacks:
226
227 // from store_client::readBody():
228 HttpReply *rep = (HttpReply *)e.getReply();
229 const ssize_t end = headersEnd(sourceBuf.data, sourceBuf.length);
230 if (!rep->parseCharBuf(sourceBuf.data, end)) {
231 debugs(20, DBG_IMPORTANT, "Could not parse mem-cached headers: " << e);
232 return false;
233 }
234 // local memory stores both headers and body
235 e.mem_obj->object_sz = sourceBuf.length; // from StoreEntry::complete()
236
237 storeGetMemSpace(sourceBuf.length); // from StoreEntry::write()
238
239 assert(e.mem_obj->data_hdr.write(sourceBuf)); // from MemObject::write()
240 const int64_t written = e.mem_obj->endOffset();
30204d23
AR
241 // we should write all because StoreEntry::write() never fails
242 assert(written >= 0 &&
243 static_cast<size_t>(written) == sourceBuf.length);
9487bae9
AR
244 // would be nice to call validLength() here, but it needs e.key
245
246 debugs(20, 7, HERE << "mem-loaded all " << written << " bytes of " << e <<
247 " from " << page);
248
249 e.hideMemObject();
250
251 return true;
252}
253
254void
255MemStore::considerKeeping(StoreEntry &e)
256{
257 if (!e.memoryCachable()) {
258 debugs(20, 7, HERE << "Not memory cachable: " << e);
259 return; // cannot keep due to entry state or properties
260 }
261
262 assert(e.mem_obj);
263 if (!willFit(e.mem_obj->endOffset())) {
264 debugs(20, 5, HERE << "No mem-cache space for " << e);
265 return; // failed to free enough space
266 }
267
268 keep(e); // may still fail
269}
270
271bool
272MemStore::willFit(int64_t need)
273{
274 // TODO: obey configured maximum entry size (with page-based rounding)
30204d23 275 return need <= static_cast<int64_t>(Ipc::Mem::PageSize());
9487bae9
AR
276}
277
278/// allocates map slot and calls copyToShm to store the entry in shared memory
279void
280MemStore::keep(StoreEntry &e)
281{
282 if (!map) {
283 debugs(20, 5, HERE << "No map to mem-cache " << e);
284 return;
285 }
286
287 sfileno index = 0;
288 Ipc::StoreMapSlot *slot = map->openForWriting(reinterpret_cast<const cache_key *>(e.key), index);
289 if (!slot) {
290 debugs(20, 5, HERE << "No room in mem-cache map to index " << e);
291 return;
292 }
293
294 MemStoreMap::Extras &extras = map->extras(index);
295 if (copyToShm(e, extras)) {
296 slot->set(e);
297 map->closeForWriting(index, false);
298 } else {
299 map->abortIo(index);
300 }
301}
302
303/// uses mem_hdr::copy() to copy local data to shared memory
304bool
305MemStore::copyToShm(StoreEntry &e, MemStoreMap::Extras &extras)
306{
307 Ipc::Mem::PageId page;
551f8a18 308 if (!Ipc::Mem::GetPage(Ipc::Mem::PageId::cachePage, page)) {
9487bae9
AR
309 debugs(20, 5, HERE << "No mem-cache page for " << e);
310 return false; // GetPage is responsible for any cleanup on failures
311 }
312
313 const int64_t bufSize = Ipc::Mem::PageSize();
314 const int64_t eSize = e.mem_obj->endOffset();
315
316 StoreIOBuffer sharedSpace(bufSize, 0,
317 static_cast<char*>(PagePointer(page)));
9199139f 318
9487bae9
AR
319 // check that we kept everything or purge incomplete/sparse cached entry
320 const ssize_t copied = e.mem_obj->data_hdr.copy(sharedSpace);
321 if (eSize != copied) {
322 debugs(20, 2, HERE << "Failed to mem-cache " << e << ": " <<
323 eSize << "!=" << copied);
324 // cleanup
325 PutPage(page);
326 return false;
327 }
328
329 debugs(20, 7, HERE << "mem-cached all " << eSize << " bytes of " << e <<
330 " in " << page);
331
17cf0a47 332 theCurrentSize += Ipc::Mem::PageSize();
9487bae9
AR
333 // remember storage location and size
334 extras.page = page;
335 extras.storedSize = copied;
336 return true;
337}
7f6748c8
AR
338
339void
340MemStore::cleanReadable(const sfileno fileno)
341{
342 Ipc::Mem::PutPage(map->extras(fileno).page);
17cf0a47 343 theCurrentSize -= Ipc::Mem::PageSize();
7f6748c8
AR
344}
345
a4555399
AR
346/// calculates maximum number of entries we need to store and map
347int64_t
348MemStore::EntryLimit()
349{
350 if (!Config.memMaxSize)
351 return 0; // no memory cache configured
352
a4555399
AR
353 const int64_t entrySize = Ipc::Mem::PageSize(); // for now
354 const int64_t entryLimit = Config.memMaxSize / entrySize;
a4555399
AR
355 return entryLimit;
356}
357
358
ea2cdeb6
DK
359/// reports our needs for shared memory pages to Ipc::Mem::Pages
360class MemStoreClaimMemoryNeedsRr: public RegisteredRunner
361{
362public:
363 /* RegisteredRunner API */
364 virtual void run(const RunnerRegistry &r);
365};
366
367RunnerRegistrationEntry(rrClaimMemoryNeeds, MemStoreClaimMemoryNeedsRr);
368
369
370void
371MemStoreClaimMemoryNeedsRr::run(const RunnerRegistry &)
372{
373 Ipc::Mem::NotePageNeed(Ipc::Mem::PageId::cachePage, MemStore::EntryLimit());
374}
375
376
a4555399 377/// initializes shared memory segments used by MemStore
4404f1c5 378class MemStoreRr: public Ipc::Mem::RegisteredRunner
a4555399
AR
379{
380public:
381 /* RegisteredRunner API */
68353d5a 382 MemStoreRr(): owner(NULL) {}
a4555399 383 virtual void run(const RunnerRegistry &);
c011f9bc 384 virtual ~MemStoreRr();
68353d5a 385
4404f1c5
DK
386protected:
387 virtual void create(const RunnerRegistry &);
388
68353d5a
DK
389private:
390 MemStoreMap::Owner *owner;
a4555399
AR
391};
392
393RunnerRegistrationEntry(rrAfterConfig, MemStoreRr);
394
395
4404f1c5 396void MemStoreRr::run(const RunnerRegistry &r)
a4555399 397{
57af1e3f
AR
398 // decide whether to use a shared memory cache if the user did not specify
399 if (!Config.memShared.configured()) {
794d4c0c 400 Config.memShared.configure(Ipc::Atomic::Enabled() &&
9199139f
AR
401 Ipc::Mem::Segment::Enabled() && UsingSmp() &&
402 Config.memMaxSize > 0);
794d4c0c 403 } else if (Config.memShared && !Ipc::Atomic::Enabled()) {
65b81b27
AR
404 // bail if the user wants shared memory cache but we cannot support it
405 fatal("memory_cache_shared is on, but no support for atomic operations detected");
9199139f 406 } else if (Config.memShared && !Ipc::Mem::Segment::Enabled()) {
c975f532 407 fatal("memory_cache_shared is on, but no support for shared memory detected");
53bbccec
DK
408 } else if (Config.memShared && !UsingSmp()) {
409 debugs(20, DBG_IMPORTANT, "WARNING: memory_cache_shared is on, but only"
410 " a single worker is running");
57af1e3f
AR
411 }
412
4404f1c5
DK
413 Ipc::Mem::RegisteredRunner::run(r);
414}
415
416void MemStoreRr::create(const RunnerRegistry &)
417{
57af1e3f 418 if (!Config.memShared)
60be8b2d 419 return;
a4555399 420
4404f1c5
DK
421 Must(!owner);
422 const int64_t entryLimit = MemStore::EntryLimit();
ea2cdeb6
DK
423 if (entryLimit <= 0) {
424 if (Config.memMaxSize > 0) {
425 debugs(20, DBG_IMPORTANT, "WARNING: mem-cache size is too small ("
426 << (Config.memMaxSize / 1024.0) << " KB), should be >= " <<
427 (Ipc::Mem::PageSize() / 1024.0) << " KB");
428 }
4404f1c5 429 return; // no memory cache configured or a misconfiguration
ea2cdeb6 430 }
4404f1c5 431 owner = MemStoreMap::Init(ShmLabel, entryLimit);
a4555399 432}
c011f9bc
DK
433
434MemStoreRr::~MemStoreRr()
435{
68353d5a 436 delete owner;
c011f9bc 437}