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