]>
Commit | Line | Data |
---|---|---|
528b2c61 | 1 | |
2 | /* | |
528b2c61 | 3 | * DEBUG: section 19 Store Memory Primitives |
4 | * AUTHOR: Robert Collins | |
5 | * | |
6 | * SQUID Web Proxy Cache http://www.squid-cache.org/ | |
7 | * ---------------------------------------------------------- | |
8 | * | |
9 | * Squid is the result of efforts by numerous individuals from | |
10 | * the Internet community; see the CONTRIBUTORS file for full | |
11 | * details. Many organizations have provided support for Squid's | |
12 | * development; see the SPONSORS file for full details. Squid is | |
13 | * Copyrighted (C) 2001 by the Regents of the University of | |
14 | * California; see the COPYRIGHT file for full details. Squid | |
15 | * incorporates software developed and/or copyrighted by other | |
16 | * sources; see the CREDITS file for full details. | |
17 | * | |
18 | * This program is free software; you can redistribute it and/or modify | |
19 | * it under the terms of the GNU General Public License as published by | |
20 | * the Free Software Foundation; either version 2 of the License, or | |
21 | * (at your option) any later version. | |
26ac0430 | 22 | * |
528b2c61 | 23 | * This program is distributed in the hope that it will be useful, |
24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
26 | * GNU General Public License for more details. | |
26ac0430 | 27 | * |
528b2c61 | 28 | * You should have received a copy of the GNU General Public License |
29 | * along with this program; if not, write to the Free Software | |
30 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
31 | * | |
32 | */ | |
33 | ||
582c2af2 | 34 | #include "squid.h" |
3e4bebf8 | 35 | #include "comm/Connection.h" |
582c2af2 | 36 | #include "Generic.h" |
af69c635 | 37 | #include "globals.h" |
528b2c61 | 38 | #include "HttpReply.h" |
582c2af2 FC |
39 | #include "HttpRequest.h" |
40 | #include "MemBuf.h" | |
41 | #include "MemObject.h" | |
42 | #include "profiler/Profiler.h" | |
4d5904f7 | 43 | #include "SquidConfig.h" |
528b2c61 | 44 | #include "Store.h" |
45 | #include "StoreClient.h" | |
582c2af2 | 46 | |
9a0a18de | 47 | #if USE_DELAY_POOLS |
b67e2c8c | 48 | #include "DelayPools.h" |
49 | #endif | |
528b2c61 | 50 | |
51 | /* TODO: make this global or private */ | |
52 | #if URL_CHECKSUM_DEBUG | |
53 | static unsigned int url_checksum(const char *url); | |
54 | unsigned int | |
55 | url_checksum(const char *url) | |
56 | { | |
57 | unsigned int ck; | |
c3031d67 | 58 | SquidMD5_CTX M; |
528b2c61 | 59 | static unsigned char digest[16]; |
c3031d67 | 60 | SquidMD5Init(&M); |
61 | SquidMD5Update(&M, (unsigned char *) url, strlen(url)); | |
62 | SquidMD5Final(digest, &M); | |
41d00cd3 | 63 | memcpy(&ck, digest, sizeof(ck)); |
528b2c61 | 64 | return ck; |
65 | } | |
62e76326 | 66 | |
528b2c61 | 67 | #endif |
68 | ||
aa839030 | 69 | RemovalPolicy * mem_policy = NULL; |
70 | ||
528b2c61 | 71 | size_t |
72 | MemObject::inUseCount() | |
73 | { | |
9f9e06f3 | 74 | return Pool().inUseCount(); |
528b2c61 | 75 | } |
76 | ||
c877c0bc AR |
77 | const char * |
78 | MemObject::storeId() const { | |
79 | if (!storeId_.defined()) { | |
80 | debugs(20, DBG_IMPORTANT, "Bug: Missing MemObject::storeId value"); | |
81 | dump(); | |
82 | storeId_ = "[unknown_URI]"; | |
83 | } | |
84 | return storeId_.termedBuf(); | |
9487bae9 AR |
85 | } |
86 | ||
c877c0bc AR |
87 | const char * |
88 | MemObject::logUri() const { | |
89 | return logUri_.defined() ? logUri_.termedBuf() : storeId(); | |
90 | } | |
4a56ee8d | 91 | |
c877c0bc AR |
92 | bool |
93 | MemObject::hasUris() const { | |
94 | return storeId_.defined(); | |
95 | } | |
4a56ee8d | 96 | |
c877c0bc AR |
97 | void |
98 | MemObject::setUris(char const *aStoreId, char const *aLogUri, const HttpRequestMethod &aMethod) | |
99 | { | |
100 | storeId_ = aStoreId; | |
101 | ||
102 | // fast pointer comparison for a common storeCreateEntry(url,url,...) case | |
103 | if (!aLogUri || aLogUri == aStoreId) | |
104 | logUri_.clean(); // use storeId_ by default to minimize copying | |
105 | else | |
106 | logUri_ = aLogUri; | |
62e76326 | 107 | |
c877c0bc | 108 | method = aMethod; |
4a56ee8d | 109 | |
c877c0bc AR |
110 | #if URL_CHECKSUM_DEBUG |
111 | chksum = url_checksum(urlXXX()); | |
528b2c61 | 112 | #endif |
c877c0bc | 113 | } |
62e76326 | 114 | |
c877c0bc AR |
115 | MemObject::MemObject(): smpCollapsed(false) |
116 | { | |
117 | debugs(20, 3, HERE << "new MemObject " << this); | |
118 | _reply = new HttpReply; | |
119 | HTTPMSGLOCK(_reply); | |
4a56ee8d | 120 | |
528b2c61 | 121 | object_sz = -1; |
4a56ee8d | 122 | |
528b2c61 | 123 | /* XXX account log_url */ |
ddc9b32c AR |
124 | |
125 | swapout.decision = SwapOut::swNeedsCheck; | |
528b2c61 | 126 | } |
127 | ||
128 | MemObject::~MemObject() | |
129 | { | |
e85137f1 | 130 | debugs(20, 3, HERE << "del MemObject " << this); |
c877c0bc | 131 | const Ctx ctx = ctx_enter(storeId_.termedBuf()); /* XXX: need URI? */ |
62e76326 | 132 | |
c877c0bc AR |
133 | #if URL_CHECKSUM_DEBUG |
134 | checkUrlChecksum(); | |
528b2c61 | 135 | #endif |
62e76326 | 136 | |
4475555f | 137 | if (!shutting_down) { // Store::Root() is FATALly missing during shutdown |
c877c0bc | 138 | // TODO: Consider moving these to destroyMemoryObject |
4475555f AR |
139 | if (xitTable.index >= 0) |
140 | Store::Root().transientsDisconnect(*this); | |
141 | if (memCache.index >= 0) | |
142 | Store::Root().memoryDisconnect(*this); | |
143 | ||
144 | assert(xitTable.index < 0); | |
145 | assert(memCache.index < 0); | |
528b2c61 | 146 | assert(swapout.sio == NULL); |
4475555f | 147 | } |
62e76326 | 148 | |
528b2c61 | 149 | data_hdr.freeContent(); |
62e76326 | 150 | |
9cdee68d | 151 | #if 0 |
528b2c61 | 152 | /* |
153 | * There is no way to abort FD-less clients, so they might | |
9cdee68d | 154 | * still have mem->clients set. |
528b2c61 | 155 | */ |
9cdee68d | 156 | assert(clients.head == NULL); |
157 | ||
158 | #endif | |
62e76326 | 159 | |
6dd9f4bd | 160 | HTTPMSGUNLOCK(_reply); |
62e76326 | 161 | |
6dd9f4bd | 162 | HTTPMSGUNLOCK(request); |
62e76326 | 163 | |
528b2c61 | 164 | ctx_exit(ctx); /* must exit before we free mem->url */ |
62e76326 | 165 | |
528b2c61 | 166 | safe_free(vary_headers); |
167 | } | |
168 | ||
169 | void | |
170 | MemObject::unlinkRequest() | |
171 | { | |
6dd9f4bd | 172 | HTTPMSGUNLOCK(request); |
528b2c61 | 173 | } |
174 | ||
175 | void | |
55759ffb | 176 | MemObject::write(const StoreIOBuffer &writeBuffer) |
528b2c61 | 177 | { |
1d5161bd | 178 | PROF_start(MemObject_write); |
4a7a3d56 | 179 | debugs(19, 6, "memWrite: offset " << writeBuffer.offset << " len " << writeBuffer.length); |
528b2c61 | 180 | |
528b2c61 | 181 | /* We don't separate out mime headers yet, so ensure that the first |
26ac0430 | 182 | * write is at offset 0 - where they start |
528b2c61 | 183 | */ |
184 | assert (data_hdr.endOffset() || writeBuffer.offset == 0); | |
185 | ||
186 | assert (data_hdr.write (writeBuffer)); | |
1d5161bd | 187 | PROF_stop(MemObject_write); |
528b2c61 | 188 | } |
189 | ||
190 | void | |
191 | MemObject::dump() const | |
192 | { | |
42a503bd | 193 | data_hdr.dump(); |
528b2c61 | 194 | #if 0 |
195 | /* do we want this one? */ | |
e0236918 | 196 | debugs(20, DBG_IMPORTANT, "MemObject->data.origin_offset: " << (data_hdr.head ? data_hdr.head->nodeBuffer.offset : 0)); |
528b2c61 | 197 | #endif |
62e76326 | 198 | |
e0236918 FC |
199 | debugs(20, DBG_IMPORTANT, "MemObject->start_ping: " << start_ping.tv_sec << "."<< std::setfill('0') << std::setw(6) << start_ping.tv_usec); |
200 | debugs(20, DBG_IMPORTANT, "MemObject->inmem_hi: " << data_hdr.endOffset()); | |
201 | debugs(20, DBG_IMPORTANT, "MemObject->inmem_lo: " << inmem_lo); | |
202 | debugs(20, DBG_IMPORTANT, "MemObject->nclients: " << nclients); | |
203 | debugs(20, DBG_IMPORTANT, "MemObject->reply: " << _reply); | |
204 | debugs(20, DBG_IMPORTANT, "MemObject->request: " << request); | |
c877c0bc AR |
205 | debugs(20, DBG_IMPORTANT, "MemObject->logUri: " << logUri_); |
206 | debugs(20, DBG_IMPORTANT, "MemObject->storeId: " << storeId_); | |
528b2c61 | 207 | } |
208 | ||
209 | HttpReply const * | |
210 | MemObject::getReply() const | |
211 | { | |
212 | return _reply; | |
213 | } | |
214 | ||
4a56ee8d | 215 | void |
216 | MemObject::replaceHttpReply(HttpReply *newrep) | |
217 | { | |
6dd9f4bd | 218 | HTTPMSGUNLOCK(_reply); |
b248c2a3 AJ |
219 | _reply = newrep; |
220 | HTTPMSGLOCK(_reply); | |
4a56ee8d | 221 | } |
222 | ||
26ac0430 AJ |
223 | struct LowestMemReader : public unary_function<store_client, void> { |
224 | LowestMemReader(int64_t seed):current(seed) {} | |
62e76326 | 225 | |
26ac0430 | 226 | void operator() (store_client const &x) { |
62e76326 | 227 | if (x.memReaderHasLowerOffset(current)) |
228 | current = x.copyInto.offset; | |
229 | } | |
230 | ||
47f6e231 | 231 | int64_t current; |
528b2c61 | 232 | }; |
233 | ||
26ac0430 AJ |
234 | struct StoreClientStats : public unary_function<store_client, void> { |
235 | StoreClientStats(MemBuf *anEntry):where(anEntry),index(0) {} | |
62e76326 | 236 | |
26ac0430 | 237 | void operator()(store_client const &x) { |
aec55359 FC |
238 | x.dumpStats(where, index); |
239 | ++index; | |
528b2c61 | 240 | } |
62e76326 | 241 | |
fcc35180 | 242 | MemBuf *where; |
528b2c61 | 243 | size_t index; |
244 | }; | |
245 | ||
246 | void | |
83af6fa2 | 247 | MemObject::stat(MemBuf * mb) const |
528b2c61 | 248 | { |
2fe7eff9 | 249 | mb->Printf("\t%s %s\n", |
c877c0bc | 250 | RequestMethodStr(method), logUri()); |
83af6fa2 AJ |
251 | if (vary_headers) |
252 | mb->Printf("\tvary_headers: %s\n", vary_headers); | |
c91ca3ce DK |
253 | mb->Printf("\tinmem_lo: %" PRId64 "\n", inmem_lo); |
254 | mb->Printf("\tinmem_hi: %" PRId64 "\n", data_hdr.endOffset()); | |
255 | mb->Printf("\tswapout: %" PRId64 " bytes queued\n", | |
47f6e231 | 256 | swapout.queue_offset); |
62e76326 | 257 | |
528b2c61 | 258 | if (swapout.sio.getRaw()) |
c91ca3ce | 259 | mb->Printf("\tswapout: %" PRId64 " bytes written\n", |
47f6e231 | 260 | (int64_t) swapout.sio->offset()); |
62e76326 | 261 | |
fcc35180 | 262 | StoreClientStats statsVisitor(mb); |
62e76326 | 263 | |
4cbb7fa8 | 264 | for_each<StoreClientStats>(clients, statsVisitor); |
528b2c61 | 265 | } |
266 | ||
47f6e231 | 267 | int64_t |
528b2c61 | 268 | MemObject::endOffset () const |
269 | { | |
270 | return data_hdr.endOffset(); | |
271 | } | |
272 | ||
3756e5c0 AR |
273 | void |
274 | MemObject::markEndOfReplyHeaders() | |
275 | { | |
276 | const int hdr_sz = endOffset(); | |
277 | assert(hdr_sz >= 0); | |
278 | assert(_reply); | |
279 | _reply->hdr_sz = hdr_sz; | |
280 | } | |
281 | ||
47f6e231 | 282 | int64_t |
528b2c61 | 283 | MemObject::size() const |
284 | { | |
62e76326 | 285 | if (object_sz < 0) |
286 | return endOffset(); | |
287 | ||
528b2c61 | 288 | return object_sz; |
289 | } | |
290 | ||
aa1a691e | 291 | int64_t |
9199139f AR |
292 | MemObject::expectedReplySize() const |
293 | { | |
aa1a691e AR |
294 | debugs(20, 7, HERE << "object_sz: " << object_sz); |
295 | if (object_sz >= 0) // complete() has been called; we know the exact answer | |
296 | return object_sz; | |
297 | ||
298 | if (_reply) { | |
299 | const int64_t clen = _reply->bodySize(method); | |
300 | debugs(20, 7, HERE << "clen: " << clen); | |
301 | if (clen >= 0 && _reply->hdr_sz > 0) // yuck: HttpMsg sets hdr_sz to 0 | |
302 | return clen + _reply->hdr_sz; | |
303 | } | |
304 | ||
305 | return -1; // not enough information to predict | |
306 | } | |
307 | ||
528b2c61 | 308 | void |
309 | MemObject::reset() | |
310 | { | |
311 | assert(swapout.sio == NULL); | |
312 | data_hdr.freeContent(); | |
313 | inmem_lo = 0; | |
314 | /* Should we check for clients? */ | |
315 | } | |
316 | ||
47f6e231 | 317 | int64_t |
528b2c61 | 318 | MemObject::lowestMemReaderOffset() const |
319 | { | |
320 | LowestMemReader lowest (endOffset() + 1); | |
321 | ||
4cbb7fa8 | 322 | for_each <LowestMemReader>(clients, lowest); |
62e76326 | 323 | |
528b2c61 | 324 | return lowest.current; |
325 | } | |
326 | ||
327 | /* XXX: This is wrong. It breaks *badly* on range combining */ | |
328 | bool | |
329 | MemObject::readAheadPolicyCanRead() const | |
330 | { | |
47f6e231 | 331 | return endOffset() - getReply()->hdr_sz < lowestMemReaderOffset() + Config.readAheadGap; |
528b2c61 | 332 | } |
333 | ||
334 | void | |
335 | MemObject::addClient(store_client *aClient) | |
336 | { | |
337 | ++nclients; | |
338 | dlinkAdd(aClient, &aClient->node, &clients); | |
339 | } | |
340 | ||
341 | #if URL_CHECKSUM_DEBUG | |
342 | void | |
343 | MemObject::checkUrlChecksum () const | |
344 | { | |
c877c0bc | 345 | assert(chksum == url_checksum(urlXXX())); |
528b2c61 | 346 | } |
62e76326 | 347 | |
528b2c61 | 348 | #endif |
349 | ||
350 | /* | |
351 | * How much of the object data is on the disk? | |
352 | */ | |
47f6e231 | 353 | int64_t |
528b2c61 | 354 | MemObject::objectBytesOnDisk() const |
355 | { | |
356 | /* | |
357 | * NOTE: storeOffset() represents the disk file size, | |
358 | * not the amount of object data on disk. | |
26ac0430 | 359 | * |
528b2c61 | 360 | * If we don't have at least 'swap_hdr_sz' bytes |
361 | * then none of the object data is on disk. | |
362 | * | |
363 | * This should still be safe if swap_hdr_sz == 0, | |
364 | * meaning we haven't even opened the swapout file | |
365 | * yet. | |
366 | */ | |
62e76326 | 367 | |
528b2c61 | 368 | if (swapout.sio.getRaw() == NULL) |
62e76326 | 369 | return 0; |
370 | ||
47f6e231 | 371 | int64_t nwritten = swapout.sio->offset(); |
62e76326 | 372 | |
ed013b6c | 373 | if (nwritten <= (int64_t)swap_hdr_sz) |
62e76326 | 374 | return 0; |
375 | ||
47f6e231 | 376 | return (nwritten - swap_hdr_sz); |
528b2c61 | 377 | } |
378 | ||
47f6e231 | 379 | int64_t |
10aeba1d | 380 | MemObject::policyLowestOffsetToKeep(bool swap) const |
528b2c61 | 381 | { |
382 | /* | |
383 | * Careful. lowest_offset can be greater than endOffset(), such | |
384 | * as in the case of a range request. | |
385 | */ | |
47f6e231 | 386 | int64_t lowest_offset = lowestMemReaderOffset(); |
62e76326 | 387 | |
528b2c61 | 388 | if (endOffset() < lowest_offset || |
ff4b33f4 | 389 | endOffset() - inmem_lo > (int64_t)Config.Store.maxInMemObjSize || |
10aeba1d | 390 | (swap && !Config.onoff.memory_cache_first)) |
62e76326 | 391 | return lowest_offset; |
392 | ||
528b2c61 | 393 | return inmem_lo; |
394 | } | |
395 | ||
396 | void | |
397 | MemObject::trimSwappable() | |
398 | { | |
10aeba1d | 399 | int64_t new_mem_lo = policyLowestOffsetToKeep(1); |
528b2c61 | 400 | /* |
401 | * We should only free up to what we know has been written | |
402 | * to disk, not what has been queued for writing. Otherwise | |
403 | * there will be a chunk of the data which is not in memory | |
404 | * and is not yet on disk. | |
405 | * The -1 makes sure the page isn't freed until storeSwapOut has | |
aa1a691e | 406 | * walked to the next page. |
528b2c61 | 407 | */ |
47f6e231 | 408 | int64_t on_disk; |
62e76326 | 409 | |
528b2c61 | 410 | if ((on_disk = objectBytesOnDisk()) - 1 < new_mem_lo) |
62e76326 | 411 | new_mem_lo = on_disk - 1; |
412 | ||
528b2c61 | 413 | if (new_mem_lo == -1) |
62e76326 | 414 | new_mem_lo = 0; /* the above might become -1 */ |
415 | ||
528b2c61 | 416 | data_hdr.freeDataUpto(new_mem_lo); |
62e76326 | 417 | |
528b2c61 | 418 | inmem_lo = new_mem_lo; |
419 | } | |
420 | ||
421 | void | |
422 | MemObject::trimUnSwappable() | |
423 | { | |
99921d9d AR |
424 | if (const int64_t new_mem_lo = policyLowestOffsetToKeep(false)) { |
425 | assert (new_mem_lo > 0); | |
426 | data_hdr.freeDataUpto(new_mem_lo); | |
427 | inmem_lo = new_mem_lo; | |
428 | } // else we should not trim anything at this time | |
528b2c61 | 429 | } |
430 | ||
528b2c61 | 431 | bool |
432 | MemObject::isContiguous() const | |
433 | { | |
47f6e231 | 434 | bool result = data_hdr.hasContigousContentRange (Range<int64_t>(inmem_lo, endOffset())); |
528b2c61 | 435 | /* XXX : make this higher level */ |
bf8fe701 | 436 | debugs (19, result ? 4 :3, "MemObject::isContiguous: Returning " << (result ? "true" : "false")); |
528b2c61 | 437 | return result; |
438 | } | |
b67e2c8c | 439 | |
440 | int | |
384a7590 | 441 | MemObject::mostBytesWanted(int max, bool ignoreDelayPools) const |
b67e2c8c | 442 | { |
9a0a18de | 443 | #if USE_DELAY_POOLS |
384a7590 JP |
444 | if (!ignoreDelayPools) { |
445 | /* identify delay id with largest allowance */ | |
446 | DelayId largestAllowance = mostBytesAllowed (); | |
447 | return largestAllowance.bytesWanted(0, max); | |
448 | } | |
449 | #endif | |
62e76326 | 450 | |
b67e2c8c | 451 | return max; |
b67e2c8c | 452 | } |
453 | ||
a46d2c0e | 454 | void |
455 | MemObject::setNoDelay(bool const newValue) | |
456 | { | |
9a0a18de | 457 | #if USE_DELAY_POOLS |
a46d2c0e | 458 | |
459 | for (dlink_node *node = clients.head; node; node = node->next) { | |
460 | store_client *sc = (store_client *) node->data; | |
461 | sc->delayId.setNoDelay(newValue); | |
462 | } | |
463 | ||
464 | #endif | |
465 | } | |
466 | ||
467 | void | |
468 | MemObject::delayRead(DeferredRead const &aRead) | |
469 | { | |
470 | deferredReads.delayRead(aRead); | |
471 | } | |
472 | ||
473 | void | |
474 | MemObject::kickReads() | |
475 | { | |
476 | deferredReads.kickReads(-1); | |
477 | } | |
478 | ||
9a0a18de | 479 | #if USE_DELAY_POOLS |
b67e2c8c | 480 | DelayId |
481 | MemObject::mostBytesAllowed() const | |
482 | { | |
483 | int j; | |
484 | int jmax = -1; | |
485 | DelayId result; | |
62e76326 | 486 | |
b67e2c8c | 487 | for (dlink_node *node = clients.head; node; node = node->next) { |
62e76326 | 488 | store_client *sc = (store_client *) node->data; |
d576a6a6 | 489 | #if 0 |
62e76326 | 490 | /* This test is invalid because the client may be writing data |
491 | * and thus will want data immediately. | |
492 | * If we include the test, there is a race condition when too much | |
493 | * data is read - if all sc's are writing when a read is scheduled. | |
494 | * XXX: fixme. | |
495 | */ | |
496 | ||
497 | if (!sc->callbackPending()) | |
498 | /* not waiting for more data */ | |
499 | continue; | |
500 | ||
d576a6a6 | 501 | #endif |
62e76326 | 502 | |
62e76326 | 503 | j = sc->delayId.bytesWanted(0, sc->copyInto.length); |
504 | ||
505 | if (j > jmax) { | |
506 | jmax = j; | |
507 | result = sc->delayId; | |
508 | } | |
b67e2c8c | 509 | } |
62e76326 | 510 | |
b67e2c8c | 511 | return result; |
512 | } | |
62e76326 | 513 | |
b67e2c8c | 514 | #endif |
5b55f1f1 CT |
515 | |
516 | int64_t | |
517 | MemObject::availableForSwapOut() const | |
518 | { | |
519 | return endOffset() - swapout.queue_offset; | |
520 | } |