]> git.ipfire.org Git - thirdparty/squid.git/blame - src/MemStore.cc
Added initial shared memory cache implementation (MemStore) and integrated it.
[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"
9#include "ipc/mem/Page.h"
10#include "ipc/mem/Pages.h"
11#include "MemObject.h"
12#include "MemStore.h"
13#include "HttpReply.h"
14
15
16// XXX: support storage using more than one page per entry
17
18
19MemStore::MemStore(): map(NULL)
20{
21}
22
23MemStore::~MemStore()
24{
25 delete map;
26}
27
28void
29MemStore::init()
30{
31 if (!map && Config.memMaxSize && (!UsingSmp() || IamWorkerProcess())) {
32 // TODO: warn if we cannot support the configured maximum entry size
33 const int64_t entrySize = Ipc::Mem::PageSize(); // for now
34 const int64_t entryCount = Config.memMaxSize / entrySize;
35 // TODO: warn if we cannot cache at least one item (misconfiguration)
36 if (entryCount > 0)
37 map = new MemStoreMap("cache_mem", entryCount);
38 }
39}
40
41void
42MemStore::stat(StoreEntry &output) const
43{
44 storeAppendPrintf(&output, "Memory Cache");
45 // TODO: implement
46}
47
48void
49MemStore::maintain()
50{
51}
52
53uint64_t
54MemStore::minSize() const
55{
56 return 0; // XXX: irrelevant, but Store parent forces us to implement this
57}
58
59uint64_t
60MemStore::maxSize() const
61{
62 return 0; // XXX: make configurable
63}
64
65void
66MemStore::updateSize(int64_t eSize, int sign)
67{
68 // XXX: irrelevant, but Store parent forces us to implement this
69 fatal("MemStore::updateSize should not be called");
70}
71
72void
73MemStore::reference(StoreEntry &)
74{
75}
76
77void
78MemStore::dereference(StoreEntry &)
79{
80}
81
82int
83MemStore::callback()
84{
85 return 0;
86}
87
88StoreSearch *
89MemStore::search(String const, HttpRequest *)
90{
91 fatal("not implemented");
92 return NULL;
93}
94
95StoreEntry *
96MemStore::get(const cache_key *key)
97{
98 if (!map)
99 return NULL;
100
101 // XXX: replace sfileno with a bigger word (sfileno is only for cache_dirs)
102 sfileno index;
103 const Ipc::StoreMapSlot *const slot = map->openForReading(key, index);
104 if (!slot)
105 return NULL;
106
107 const Ipc::StoreMapSlot::Basics &basics = slot->basics;
108 const MemStoreMap::Extras &extras = map->extras(index);
109
110 // create a brand new store entry and initialize it with stored info
111 StoreEntry *e = new StoreEntry();
112 e->lock_count = 0;
113
114 e->swap_file_sz = basics.swap_file_sz;
115 e->lastref = basics.lastref;
116 e->timestamp = basics.timestamp;
117 e->expires = basics.expires;
118 e->lastmod = basics.lastmod;
119 e->refcount = basics.refcount;
120 e->flags = basics.flags;
121
122 e->store_status = STORE_OK;
123 e->mem_status = IN_MEMORY; // setMemStatus(IN_MEMORY) requires mem_obj
124 //e->swap_status = set in StoreEntry constructor to SWAPOUT_NONE;
125 e->ping_status = PING_NONE;
126
127 EBIT_SET(e->flags, ENTRY_CACHABLE);
128 EBIT_CLR(e->flags, RELEASE_REQUEST);
129 EBIT_CLR(e->flags, KEY_PRIVATE);
130 EBIT_SET(e->flags, ENTRY_VALIDATED);
131
132 const bool copied = copyFromShm(*e, extras);
133
134 // we copied everything we could to local memory; no more need to lock
135 map->closeForReading(index);
136
137 if (copied) {
138 e->hashInsert(key);
139 return e;
140 }
141
142 debugs(20, 3, HERE << "mem-loading failed; freeing " << index);
143 map->free(index); // do not let others into the same trap
144 return NULL;
145}
146
147void
148MemStore::get(String const key, STOREGETCLIENT aCallback, void *aCallbackData)
149{
150 // XXX: not needed but Store parent forces us to implement this
151 fatal("MemStore::get(key,callback,data) should not be called");
152}
153
154bool
155MemStore::copyFromShm(StoreEntry &e, const MemStoreMap::Extras &extras)
156{
157 const Ipc::Mem::PageId &page = extras.page;
158
159 StoreIOBuffer sourceBuf(extras.storedSize, 0,
160 static_cast<char*>(PagePointer(page)));
161
162 // XXX: We do not know the URLs yet, only the key, but we need to parse and
163 // store the response for the Root().get() callers to be happy because they
164 // expect IN_MEMORY entries to already have the response headers and body.
165 // At least one caller calls createMemObject() if there is not one, so
166 // we hide the true object until that happens (to avoid leaking TBD URLs).
167 e.createMemObject("TBD", "TBD");
168
169 // emulate the usual Store code but w/o inapplicable checks and callbacks:
170
171 // from store_client::readBody():
172 HttpReply *rep = (HttpReply *)e.getReply();
173 const ssize_t end = headersEnd(sourceBuf.data, sourceBuf.length);
174 if (!rep->parseCharBuf(sourceBuf.data, end)) {
175 debugs(20, DBG_IMPORTANT, "Could not parse mem-cached headers: " << e);
176 return false;
177 }
178 // local memory stores both headers and body
179 e.mem_obj->object_sz = sourceBuf.length; // from StoreEntry::complete()
180
181 storeGetMemSpace(sourceBuf.length); // from StoreEntry::write()
182
183 assert(e.mem_obj->data_hdr.write(sourceBuf)); // from MemObject::write()
184 const int64_t written = e.mem_obj->endOffset();
185 assert(written == sourceBuf.length); // StoreEntry::write never fails?
186 // would be nice to call validLength() here, but it needs e.key
187
188 debugs(20, 7, HERE << "mem-loaded all " << written << " bytes of " << e <<
189 " from " << page);
190
191 e.hideMemObject();
192
193 return true;
194}
195
196void
197MemStore::considerKeeping(StoreEntry &e)
198{
199 if (!e.memoryCachable()) {
200 debugs(20, 7, HERE << "Not memory cachable: " << e);
201 return; // cannot keep due to entry state or properties
202 }
203
204 assert(e.mem_obj);
205 if (!willFit(e.mem_obj->endOffset())) {
206 debugs(20, 5, HERE << "No mem-cache space for " << e);
207 return; // failed to free enough space
208 }
209
210 keep(e); // may still fail
211}
212
213bool
214MemStore::willFit(int64_t need)
215{
216 // TODO: obey configured maximum entry size (with page-based rounding)
217 return need <= Ipc::Mem::PageSize();
218}
219
220/// allocates map slot and calls copyToShm to store the entry in shared memory
221void
222MemStore::keep(StoreEntry &e)
223{
224 if (!map) {
225 debugs(20, 5, HERE << "No map to mem-cache " << e);
226 return;
227 }
228
229 sfileno index = 0;
230 Ipc::StoreMapSlot *slot = map->openForWriting(reinterpret_cast<const cache_key *>(e.key), index);
231 if (!slot) {
232 debugs(20, 5, HERE << "No room in mem-cache map to index " << e);
233 return;
234 }
235
236 MemStoreMap::Extras &extras = map->extras(index);
237 if (copyToShm(e, extras)) {
238 slot->set(e);
239 map->closeForWriting(index, false);
240 } else {
241 map->abortIo(index);
242 }
243}
244
245/// uses mem_hdr::copy() to copy local data to shared memory
246bool
247MemStore::copyToShm(StoreEntry &e, MemStoreMap::Extras &extras)
248{
249 Ipc::Mem::PageId page;
250 if (!Ipc::Mem::GetPage(page)) {
251 debugs(20, 5, HERE << "No mem-cache page for " << e);
252 return false; // GetPage is responsible for any cleanup on failures
253 }
254
255 const int64_t bufSize = Ipc::Mem::PageSize();
256 const int64_t eSize = e.mem_obj->endOffset();
257
258 StoreIOBuffer sharedSpace(bufSize, 0,
259 static_cast<char*>(PagePointer(page)));
260
261 // check that we kept everything or purge incomplete/sparse cached entry
262 const ssize_t copied = e.mem_obj->data_hdr.copy(sharedSpace);
263 if (eSize != copied) {
264 debugs(20, 2, HERE << "Failed to mem-cache " << e << ": " <<
265 eSize << "!=" << copied);
266 // cleanup
267 PutPage(page);
268 return false;
269 }
270
271 debugs(20, 7, HERE << "mem-cached all " << eSize << " bytes of " << e <<
272 " in " << page);
273
274 // remember storage location and size
275 extras.page = page;
276 extras.storedSize = copied;
277 return true;
278}