]>
Commit | Line | Data |
---|---|---|
9cef6668 | 1 | /* |
bde978a6 | 2 | * Copyright (C) 1996-2015 The Squid Software Foundation and contributors |
26ac0430 | 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. | |
9cef6668 | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 90 Storage Manager Client-Side Interface */ |
10 | ||
582c2af2 | 11 | #include "squid.h" |
a553a5a3 | 12 | #include "event.h" |
22bbd840 | 13 | #include "globals.h" |
528b2c61 | 14 | #include "HttpReply.h" |
582c2af2 FC |
15 | #include "HttpRequest.h" |
16 | #include "MemBuf.h" | |
528b2c61 | 17 | #include "MemObject.h" |
b6149797 | 18 | #include "mime_header.h" |
582c2af2 | 19 | #include "profiler/Profiler.h" |
4d5904f7 | 20 | #include "SquidConfig.h" |
e4f1fdae | 21 | #include "StatCounters.h" |
582c2af2 | 22 | #include "Store.h" |
f82b5c64 | 23 | #include "store_swapin.h" |
602d9612 | 24 | #include "StoreClient.h" |
528b2c61 | 25 | #include "StoreMeta.h" |
26 | #include "StoreMetaUnpacker.h" | |
9a0a18de | 27 | #if USE_DELAY_POOLS |
b67e2c8c | 28 | #include "DelayPools.h" |
29 | #endif | |
c8be6d7b | 30 | |
e3ef2b09 | 31 | /* |
32 | * NOTE: 'Header' refers to the swapfile metadata header. | |
f53969cc SM |
33 | * 'OBJHeader' refers to the object header, with cannonical |
34 | * processed object headers (which may derive from FTP/HTTP etc | |
35 | * upstream protocols | |
e3ef2b09 | 36 | * 'Body' refers to the swapfile body, which is the full |
37 | * HTTP reply (including HTTP headers and body). | |
38 | */ | |
4fcc8876 | 39 | static StoreIOState::STRCB storeClientReadBody; |
40 | static StoreIOState::STRCB storeClientReadHeader; | |
f09f5b26 | 41 | static void storeClientCopy2(StoreEntry * e, store_client * sc); |
d6f51e3c | 42 | static EVH storeClientCopyEvent; |
ae6568e7 | 43 | static bool CheckQuickAbortIsReasonable(StoreEntry * entry); |
77b32a34 | 44 | static void CheckQuickAbort(StoreEntry * entry); |
f09f5b26 | 45 | |
b001e822 | 46 | CBDATA_CLASS_INIT(store_client); |
528b2c61 | 47 | |
528b2c61 | 48 | bool |
47f6e231 | 49 | store_client::memReaderHasLowerOffset(int64_t anOffset) const |
528b2c61 | 50 | { |
51 | return getType() == STORE_MEM_CLIENT && copyInto.offset < anOffset; | |
52 | } | |
53 | ||
54 | int | |
55 | store_client::getType() const | |
56 | { | |
57 | return type; | |
58 | } | |
59 | ||
06d2839d | 60 | #if STORE_CLIENT_LIST_DEBUG |
fa80a8ef | 61 | static store_client * |
f09f5b26 | 62 | storeClientListSearch(const MemObject * mem, void *data) |
63 | { | |
06d2839d | 64 | dlink_node *node; |
65 | store_client *sc = NULL; | |
62e76326 | 66 | |
06d2839d | 67 | for (node = mem->clients.head; node; node = node->next) { |
62e76326 | 68 | sc = node->data; |
69 | ||
70 | if (sc->owner == data) | |
71 | return sc; | |
f09f5b26 | 72 | } |
62e76326 | 73 | |
06d2839d | 74 | return NULL; |
f09f5b26 | 75 | } |
c8be6d7b | 76 | |
77 | int | |
78 | storeClientIsThisAClient(store_client * sc, void *someClient) | |
79 | { | |
80 | return sc->owner == someClient; | |
81 | } | |
62e76326 | 82 | |
06d2839d | 83 | #endif |
924f73bc | 84 | #include "HttpRequest.h" |
f09f5b26 | 85 | |
86 | /* add client with fd to client list */ | |
06d2839d | 87 | store_client * |
f09f5b26 | 88 | storeClientListAdd(StoreEntry * e, void *data) |
89 | { | |
90 | MemObject *mem = e->mem_obj; | |
f09f5b26 | 91 | store_client *sc; |
92 | assert(mem); | |
06d2839d | 93 | #if STORE_CLIENT_LIST_DEBUG |
62e76326 | 94 | |
f09f5b26 | 95 | if (storeClientListSearch(mem, data) != NULL) |
62e76326 | 96 | /* XXX die! */ |
97 | assert(1 == 0); | |
98 | ||
6b8e7481 | 99 | #endif |
62e76326 | 100 | |
528b2c61 | 101 | sc = new store_client (e); |
62e76326 | 102 | |
528b2c61 | 103 | mem->addClient(sc); |
62e76326 | 104 | |
06d2839d | 105 | return sc; |
f09f5b26 | 106 | } |
107 | ||
528b2c61 | 108 | void |
109 | store_client::callback(ssize_t sz, bool error) | |
b04e66e0 | 110 | { |
63326655 | 111 | size_t bSz = 0; |
62e76326 | 112 | |
63326655 AJ |
113 | if (sz >= 0 && !error) |
114 | bSz = sz; | |
115 | ||
116 | StoreIOBuffer result(bSz, 0 ,copyInto.data); | |
117 | ||
118 | if (sz < 0 || error) | |
62e76326 | 119 | result.flags.error = 1; |
62e76326 | 120 | |
528b2c61 | 121 | result.offset = cmp_offset; |
90703668 | 122 | assert(_callback.pending()); |
63326655 | 123 | cmp_offset = copyInto.offset + bSz; |
528b2c61 | 124 | STCB *temphandler = _callback.callback_handler; |
125 | void *cbdata = _callback.callback_data; | |
126 | _callback = Callback(NULL, NULL); | |
127 | copyInto.data = NULL; | |
62e76326 | 128 | |
528b2c61 | 129 | if (cbdataReferenceValid(cbdata)) |
62e76326 | 130 | temphandler(cbdata, result); |
131 | ||
528b2c61 | 132 | cbdataReferenceDone(cbdata); |
b04e66e0 | 133 | } |
134 | ||
f115fadd | 135 | static void |
136 | storeClientCopyEvent(void *data) | |
137 | { | |
e6ccf245 | 138 | store_client *sc = (store_client *)data; |
bf8fe701 | 139 | debugs(90, 3, "storeClientCopyEvent: Running"); |
528b2c61 | 140 | assert (sc->flags.copy_event_pending); |
3dd52a0b | 141 | sc->flags.copy_event_pending = false; |
62e76326 | 142 | |
90703668 | 143 | if (!sc->_callback.pending()) |
62e76326 | 144 | return; |
145 | ||
f115fadd | 146 | storeClientCopy2(sc->entry, sc); |
f115fadd | 147 | } |
148 | ||
b67e2c8c | 149 | store_client::store_client(StoreEntry *e) : entry (e) |
9a0a18de | 150 | #if USE_DELAY_POOLS |
f53969cc | 151 | , delayId() |
b67e2c8c | 152 | #endif |
f53969cc SM |
153 | , type (e->storeClientType()) |
154 | , object_ok(true) | |
528b2c61 | 155 | { |
156 | cmp_offset = 0; | |
3dd52a0b | 157 | flags.disk_io_pending = false; |
5db6bf73 | 158 | ++ entry->refcount; |
62e76326 | 159 | |
528b2c61 | 160 | if (getType() == STORE_DISK_CLIENT) |
62e76326 | 161 | /* assert we'll be able to get the data we want */ |
02a2d80b | 162 | /* maybe we should open swapin_sio here */ |
5b55f1f1 | 163 | assert(entry->swap_filen > -1 || entry->swappingOut()); |
62e76326 | 164 | |
528b2c61 | 165 | #if STORE_CLIENT_LIST_DEBUG |
62e76326 | 166 | |
528b2c61 | 167 | owner = cbdataReference(data); |
62e76326 | 168 | |
528b2c61 | 169 | #endif |
170 | } | |
171 | ||
2f44bd34 | 172 | store_client::~store_client() |
62e76326 | 173 | {} |
2f44bd34 | 174 | |
f09f5b26 | 175 | /* copy bytes requested by the client */ |
176 | void | |
a4b8110e | 177 | storeClientCopy(store_client * sc, |
62e76326 | 178 | StoreEntry * e, |
179 | StoreIOBuffer copyInto, | |
180 | STCB * callback, | |
181 | void *data) | |
f09f5b26 | 182 | { |
528b2c61 | 183 | assert (sc != NULL); |
184 | sc->copy(e, copyInto,callback,data); | |
185 | } | |
186 | ||
187 | void | |
188 | store_client::copy(StoreEntry * anEntry, | |
62e76326 | 189 | StoreIOBuffer copyRequest, |
190 | STCB * callback_fn, | |
191 | void *data) | |
528b2c61 | 192 | { |
193 | assert (anEntry == entry); | |
194 | assert (callback_fn); | |
195 | assert (data); | |
196 | assert(!EBIT_TEST(entry->flags, ENTRY_ABORTED)); | |
bf8fe701 | 197 | debugs(90, 3, "store_client::copy: " << entry->getMD5Text() << ", from " << |
47f6e231 | 198 | copyRequest.offset << ", for length " << |
bf8fe701 | 199 | (int) copyRequest.length << ", cb " << callback_fn << ", cbdata " << |
200 | data); | |
201 | ||
06d2839d | 202 | #if STORE_CLIENT_LIST_DEBUG |
62e76326 | 203 | |
528b2c61 | 204 | assert(this == storeClientListSearch(entry->mem_obj, data)); |
06d2839d | 205 | #endif |
62e76326 | 206 | |
90703668 | 207 | assert(!_callback.pending()); |
528b2c61 | 208 | #if ONLYCONTIGUOUSREQUESTS |
62e76326 | 209 | |
528b2c61 | 210 | assert(cmp_offset == copyRequest.offset); |
211 | #endif | |
212 | /* range requests will skip into the body */ | |
213 | cmp_offset = copyRequest.offset; | |
214 | _callback = Callback (callback_fn, cbdataReference(data)); | |
215 | copyInto.data = copyRequest.data; | |
216 | copyInto.length = copyRequest.length; | |
217 | copyInto.offset = copyRequest.offset; | |
218 | ||
1d5161bd | 219 | static bool copying (false); |
220 | assert (!copying); | |
221 | copying = true; | |
222 | PROF_start(storeClient_kickReads); | |
a46d2c0e | 223 | /* we might be blocking comm reads due to readahead limits |
224 | * now we have a new offset, trigger those reads... | |
225 | */ | |
226 | entry->mem_obj->kickReads(); | |
1d5161bd | 227 | PROF_stop(storeClient_kickReads); |
228 | copying = false; | |
a46d2c0e | 229 | |
4475555f AR |
230 | anEntry->lock("store_client::copy"); // see deletion note below |
231 | ||
528b2c61 | 232 | storeClientCopy2(entry, this); |
0ad2b63b | 233 | |
4475555f AR |
234 | // Bug 3480: This store_client object may be deleted now if, for example, |
235 | // the client rejects the hit response copied above. Use on-stack pointers! | |
236 | ||
0ad2b63b | 237 | #if USE_ADAPTATION |
4475555f | 238 | anEntry->kickProducer(); |
0ad2b63b | 239 | #endif |
4475555f | 240 | anEntry->unlock("store_client::copy"); |
1809cebd | 241 | |
1809cebd | 242 | // Add no code here. This object may no longer exist. |
f09f5b26 | 243 | } |
244 | ||
f25d697f AR |
245 | /// Whether there is (or will be) more entry data for us. |
246 | bool | |
247 | store_client::moreToSend() const | |
07304bf9 | 248 | { |
f25d697f AR |
249 | if (entry->store_status == STORE_PENDING) |
250 | return true; // there may be more coming | |
251 | ||
252 | /* STORE_OK, including aborted entries: no more data is coming */ | |
253 | ||
254 | const int64_t len = entry->objectLen(); | |
62e76326 | 255 | |
0cdcf3d7 AR |
256 | // If we do not know the entry length, then we have to open the swap file. |
257 | const bool canSwapIn = entry->swap_filen >= 0; | |
f25d697f AR |
258 | if (len < 0) |
259 | return canSwapIn; | |
62e76326 | 260 | |
f25d697f AR |
261 | if (copyInto.offset >= len) |
262 | return false; // sent everything there is | |
62e76326 | 263 | |
f25d697f AR |
264 | if (canSwapIn) |
265 | return true; // if we lack prefix, we can swap it in | |
62e76326 | 266 | |
f25d697f | 267 | // If we cannot swap in, make sure we have what we want in RAM. Otherwise, |
0cdcf3d7 | 268 | // scheduleRead calls scheduleDiskRead which asserts without a swap file. |
f25d697f AR |
269 | const MemObject *mem = entry->mem_obj; |
270 | return mem && | |
9d4e9cfb | 271 | mem->inmem_lo <= copyInto.offset && copyInto.offset < mem->endOffset(); |
07304bf9 | 272 | } |
273 | ||
f09f5b26 | 274 | static void |
275 | storeClientCopy2(StoreEntry * e, store_client * sc) | |
276 | { | |
528b2c61 | 277 | /* reentrancy not allowed - note this could lead to |
278 | * dropped events | |
279 | */ | |
62e76326 | 280 | |
fa80a8ef | 281 | if (sc->flags.copy_event_pending) { |
62e76326 | 282 | return; |
fa80a8ef | 283 | } |
62e76326 | 284 | |
db1cd23c | 285 | if (EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) { |
bf8fe701 | 286 | debugs(90, 5, "storeClientCopy2: returning because ENTRY_FWD_HDR_WAIT set"); |
62e76326 | 287 | return; |
db1cd23c | 288 | } |
62e76326 | 289 | |
67fd69de | 290 | if (sc->flags.store_copying) { |
3dd52a0b | 291 | sc->flags.copy_event_pending = true; |
bf8fe701 | 292 | debugs(90, 3, "storeClientCopy2: Queueing storeClientCopyEvent()"); |
62e76326 | 293 | eventAdd("storeClientCopyEvent", storeClientCopyEvent, sc, 0.0, 0); |
294 | return; | |
67fd69de | 295 | } |
62e76326 | 296 | |
bf8fe701 | 297 | debugs(90, 3, "storeClientCopy2: " << e->getMD5Text()); |
90703668 | 298 | assert(sc->_callback.pending()); |
0bb129ee | 299 | /* |
b7fe0ab0 | 300 | * We used to check for ENTRY_ABORTED here. But there were some |
0bb129ee | 301 | * problems. For example, we might have a slow client (or two) and |
d5430dc8 AJ |
302 | * the peer server is reading far ahead and swapping to disk. Even |
303 | * if the peer aborts, we want to give the client(s) | |
0bb129ee | 304 | * everything we got before the abort condition occurred. |
305 | */ | |
528b2c61 | 306 | /* Warning: doCopy may indirectly free itself in callbacks, |
7e6b941f | 307 | * hence the lock to keep it active for the duration of |
fa80a8ef | 308 | * this function |
1809cebd AR |
309 | * XXX: Locking does not prevent calling sc destructor (it only prevents |
310 | * freeing sc memory) so sc may become invalid from C++ p.o.v. | |
fa80a8ef | 311 | */ |
ffc6d4e9 | 312 | CbcPointer<store_client> tmpLock = sc; |
3dd52a0b | 313 | assert (!sc->flags.store_copying); |
528b2c61 | 314 | sc->doCopy(e); |
ffc6d4e9 | 315 | assert(!sc->flags.store_copying); |
cfac48c2 | 316 | } |
317 | ||
528b2c61 | 318 | void |
319 | store_client::doCopy(StoreEntry *anEntry) | |
cfac48c2 | 320 | { |
528b2c61 | 321 | assert (anEntry == entry); |
3dd52a0b | 322 | flags.store_copying = true; |
528b2c61 | 323 | MemObject *mem = entry->mem_obj; |
cd748f27 | 324 | |
bf8fe701 | 325 | debugs(33, 5, "store_client::doCopy: co: " << |
47f6e231 | 326 | copyInto.offset << ", hi: " << |
327 | mem->endOffset()); | |
add2192d | 328 | |
f25d697f | 329 | if (!moreToSend()) { |
62e76326 | 330 | /* There is no more to send! */ |
26ac0430 | 331 | debugs(33, 3, HERE << "There is no more to send!"); |
62e76326 | 332 | callback(0); |
3dd52a0b | 333 | flags.store_copying = false; |
62e76326 | 334 | return; |
cfac48c2 | 335 | } |
62e76326 | 336 | |
add2192d | 337 | /* Check that we actually have data */ |
528b2c61 | 338 | if (anEntry->store_status == STORE_PENDING && copyInto.offset >= mem->endOffset()) { |
bf8fe701 | 339 | debugs(90, 3, "store_client::doCopy: Waiting for more"); |
3dd52a0b | 340 | flags.store_copying = false; |
62e76326 | 341 | return; |
cfac48c2 | 342 | } |
62e76326 | 343 | |
cfac48c2 | 344 | /* |
345 | * Slight weirdness here. We open a swapin file for any | |
346 | * STORE_DISK_CLIENT, even if we can copy the requested chunk | |
347 | * from memory in the next block. We must try to open the | |
348 | * swapin file before sending any data to the client side. If | |
349 | * we postpone the open, and then can not open the file later | |
350 | * on, the client loses big time. Its transfer just gets cut | |
351 | * off. Better to open it early (while the client side handler | |
352 | * is clientCacheHit) so that we can fall back to a cache miss | |
353 | * if needed. | |
354 | */ | |
fa80a8ef | 355 | |
0cdcf3d7 AR |
356 | if (STORE_DISK_CLIENT == getType() && swapin_sio == NULL) { |
357 | if (!startSwapin()) | |
358 | return; // failure | |
359 | } | |
360 | scheduleRead(); | |
4e70dae3 | 361 | } |
362 | ||
0cdcf3d7 AR |
363 | /// opens the swapin "file" if possible; otherwise, fail()s and returns false |
364 | bool | |
4e70dae3 | 365 | store_client::startSwapin() |
366 | { | |
bf8fe701 | 367 | debugs(90, 3, "store_client::doCopy: Need to open swap in file"); |
4e70dae3 | 368 | /* gotta open the swapin file */ |
369 | ||
370 | if (storeTooManyDiskFilesOpen()) { | |
371 | /* yuck -- this causes a TCP_SWAPFAIL_MISS on the client side */ | |
372 | fail(); | |
3dd52a0b | 373 | flags.store_copying = false; |
0cdcf3d7 | 374 | return false; |
4e70dae3 | 375 | } else if (!flags.disk_io_pending) { |
376 | /* Don't set store_io_pending here */ | |
377 | storeSwapInStart(this); | |
62e76326 | 378 | |
85a4b153 | 379 | if (swapin_sio == NULL) { |
62e76326 | 380 | fail(); |
3dd52a0b | 381 | flags.store_copying = false; |
0cdcf3d7 | 382 | return false; |
62e76326 | 383 | } |
62e76326 | 384 | |
0cdcf3d7 | 385 | return true; |
4e70dae3 | 386 | } else { |
e0236918 | 387 | debugs(90, DBG_IMPORTANT, "WARNING: Averted multiple fd operation (1)"); |
3dd52a0b | 388 | flags.store_copying = false; |
0cdcf3d7 | 389 | return false; |
cfac48c2 | 390 | } |
4e70dae3 | 391 | } |
62e76326 | 392 | |
4e70dae3 | 393 | void |
394 | store_client::scheduleRead() | |
395 | { | |
396 | MemObject *mem = entry->mem_obj; | |
397 | ||
398 | if (copyInto.offset >= mem->inmem_lo && copyInto.offset < mem->endOffset()) | |
399 | scheduleMemRead(); | |
400 | else | |
401 | scheduleDiskRead(); | |
402 | } | |
403 | ||
404 | void | |
405 | store_client::scheduleDiskRead() | |
406 | { | |
cd748f27 | 407 | /* What the client wants is not in memory. Schedule a disk read */ |
0cdcf3d7 AR |
408 | if (getType() == STORE_DISK_CLIENT) { |
409 | // we should have called startSwapin() already | |
410 | assert(swapin_sio != NULL); | |
2da4bfe6 | 411 | } else if (!swapin_sio && !startSwapin()) { |
0cdcf3d7 AR |
412 | debugs(90, 3, "bailing after swapin start failure for " << *entry); |
413 | assert(!flags.store_copying); | |
414 | return; | |
415 | } | |
62e76326 | 416 | |
528b2c61 | 417 | assert(!flags.disk_io_pending); |
62e76326 | 418 | |
0cdcf3d7 | 419 | debugs(90, 3, "reading " << *entry << " from disk"); |
62e76326 | 420 | |
528b2c61 | 421 | fileRead(); |
62e76326 | 422 | |
3dd52a0b | 423 | flags.store_copying = false; |
f09f5b26 | 424 | } |
425 | ||
4e70dae3 | 426 | void |
427 | store_client::scheduleMemRead() | |
428 | { | |
429 | /* What the client wants is in memory */ | |
430 | /* Old style */ | |
bf8fe701 | 431 | debugs(90, 3, "store_client::doCopy: Copying normal from memory"); |
90703668 | 432 | size_t sz = entry->mem_obj->data_hdr.copy(copyInto); |
4e70dae3 | 433 | callback(sz); |
3dd52a0b | 434 | flags.store_copying = false; |
4e70dae3 | 435 | } |
436 | ||
528b2c61 | 437 | void |
438 | store_client::fileRead() | |
f09f5b26 | 439 | { |
528b2c61 | 440 | MemObject *mem = entry->mem_obj; |
441 | ||
90703668 | 442 | assert(_callback.pending()); |
528b2c61 | 443 | assert(!flags.disk_io_pending); |
3dd52a0b | 444 | flags.disk_io_pending = true; |
62e76326 | 445 | |
528b2c61 | 446 | if (mem->swap_hdr_sz != 0) |
62e76326 | 447 | if (entry->swap_status == SWAPOUT_WRITING) |
47f6e231 | 448 | assert(mem->swapout.sio->offset() > copyInto.offset + (int64_t)mem->swap_hdr_sz); |
62e76326 | 449 | |
528b2c61 | 450 | storeRead(swapin_sio, |
62e76326 | 451 | copyInto.data, |
452 | copyInto.length, | |
453 | copyInto.offset + mem->swap_hdr_sz, | |
454 | mem->swap_hdr_sz == 0 ? storeClientReadHeader | |
455 | : storeClientReadBody, | |
456 | this); | |
f09f5b26 | 457 | } |
458 | ||
beae59b0 | 459 | void |
ced8def3 | 460 | store_client::readBody(const char *, ssize_t len) |
beae59b0 HN |
461 | { |
462 | int parsed_header = 0; | |
463 | ||
464 | // Don't assert disk_io_pending here.. may be called by read_header | |
3dd52a0b | 465 | flags.disk_io_pending = false; |
beae59b0 | 466 | assert(_callback.pending()); |
4a7a3d56 | 467 | debugs(90, 3, "storeClientReadBody: len " << len << ""); |
62e76326 | 468 | |
9b769c67 | 469 | if (copyInto.offset == 0 && len > 0 && entry->getReply()->sline.status() == Http::scNone) { |
62e76326 | 470 | /* Our structure ! */ |
beae59b0 | 471 | HttpReply *rep = (HttpReply *) entry->getReply(); // bypass const |
06a5ae20 | 472 | |
beae59b0 | 473 | if (!rep->parseCharBuf(copyInto.data, headersEnd(copyInto.data, len))) { |
fa84c01d | 474 | debugs(90, DBG_CRITICAL, "Could not parse headers from on disk object"); |
beae59b0 | 475 | } else { |
3196f526 HN |
476 | parsed_header = 1; |
477 | } | |
06a5ae20 | 478 | } |
62e76326 | 479 | |
beae59b0 | 480 | const HttpReply *rep = entry->getReply(); |
ff4b33f4 | 481 | if (len > 0 && rep && entry->mem_obj->inmem_lo == 0 && entry->objectLen() <= (int64_t)Config.Store.maxInMemObjSize && Config.onoff.memory_cache_disk) { |
6d3c2758 HN |
482 | storeGetMemSpace(len); |
483 | // The above may start to free our object so we need to check again | |
484 | if (entry->mem_obj->inmem_lo == 0) { | |
485 | /* Copy read data back into memory. | |
55759ffb | 486 | * copyInto.offset includes headers, which is what mem cache needs |
6d3c2758 | 487 | */ |
619da1e9 | 488 | int64_t mem_offset = entry->mem_obj->endOffset(); |
6d3c2758 | 489 | if ((copyInto.offset == mem_offset) || (parsed_header && mem_offset == rep->hdr_sz)) { |
55759ffb | 490 | entry->mem_obj->write(StoreIOBuffer(len, copyInto.offset, copyInto.data)); |
6d3c2758 | 491 | } |
3196f526 | 492 | } |
beae59b0 HN |
493 | } |
494 | ||
495 | callback(len); | |
528b2c61 | 496 | } |
497 | ||
498 | void | |
62e76326 | 499 | store_client::fail() |
528b2c61 | 500 | { |
501 | object_ok = false; | |
db2ff94c | 502 | /* synchronous open failures callback from the store, |
503 | * before startSwapin detects the failure. | |
504 | * TODO: fix this inconsistent behaviour - probably by | |
26ac0430 | 505 | * having storeSwapInStart become a callback functions, |
db2ff94c | 506 | * not synchronous |
507 | */ | |
508 | ||
90703668 | 509 | if (_callback.pending()) |
db2ff94c | 510 | callback(0, true); |
f09f5b26 | 511 | } |
512 | ||
e3ef2b09 | 513 | static void |
ced8def3 | 514 | storeClientReadHeader(void *data, const char *buf, ssize_t len, StoreIOState::Pointer) |
e3ef2b09 | 515 | { |
e6ccf245 | 516 | store_client *sc = (store_client *)data; |
528b2c61 | 517 | sc->readHeader(buf, len); |
518 | } | |
519 | ||
beae59b0 | 520 | static void |
ced8def3 | 521 | storeClientReadBody(void *data, const char *buf, ssize_t len, StoreIOState::Pointer) |
beae59b0 HN |
522 | { |
523 | store_client *sc = (store_client *)data; | |
524 | sc->readBody(buf, len); | |
525 | } | |
526 | ||
528b2c61 | 527 | void |
528 | store_client::unpackHeader(char const *buf, ssize_t len) | |
529 | { | |
4a7a3d56 | 530 | debugs(90, 3, "store_client::unpackHeader: len " << len << ""); |
62e76326 | 531 | |
e3ef2b09 | 532 | if (len < 0) { |
bf8fe701 | 533 | debugs(90, 3, "store_client::unpackHeader: " << xstrerror() << ""); |
62e76326 | 534 | fail(); |
535 | return; | |
e3ef2b09 | 536 | } |
62e76326 | 537 | |
528b2c61 | 538 | int swap_hdr_sz = 0; |
539 | StoreMetaUnpacker aBuilder(buf, len, &swap_hdr_sz); | |
62e76326 | 540 | |
528b2c61 | 541 | if (!aBuilder.isBufferSane()) { |
62e76326 | 542 | /* oops, bad disk file? */ |
e0236918 | 543 | debugs(90, DBG_IMPORTANT, "WARNING: swapfile header inconsistent with available data"); |
62e76326 | 544 | fail(); |
545 | return; | |
9bc73deb | 546 | } |
62e76326 | 547 | |
528b2c61 | 548 | tlv *tlv_list = aBuilder.createStoreMeta (); |
62e76326 | 549 | |
e3ef2b09 | 550 | if (tlv_list == NULL) { |
e0236918 | 551 | debugs(90, DBG_IMPORTANT, "WARNING: failed to unpack meta data"); |
62e76326 | 552 | fail(); |
553 | return; | |
e3ef2b09 | 554 | } |
62e76326 | 555 | |
e3ef2b09 | 556 | /* |
7e3ce7b9 | 557 | * Check the meta data and make sure we got the right object. |
e3ef2b09 | 558 | */ |
528b2c61 | 559 | for (tlv *t = tlv_list; t; t = t->next) { |
62e76326 | 560 | if (!t->checkConsistency(entry)) { |
561 | storeSwapTLVFree(tlv_list); | |
562 | fail(); | |
563 | return; | |
564 | } | |
7e3ce7b9 | 565 | } |
62e76326 | 566 | |
07304bf9 | 567 | storeSwapTLVFree(tlv_list); |
528b2c61 | 568 | |
aa1a691e | 569 | assert(swap_hdr_sz >= 0); |
528b2c61 | 570 | entry->mem_obj->swap_hdr_sz = swap_hdr_sz; |
3587dde2 AR |
571 | if (entry->swap_file_sz > 0) { // collapsed hits may not know swap_file_sz |
572 | assert(entry->swap_file_sz >= static_cast<uint64_t>(swap_hdr_sz)); | |
573 | entry->mem_obj->object_sz = entry->swap_file_sz - swap_hdr_sz; | |
574 | } | |
aa1a691e AR |
575 | debugs(90, 5, "store_client::unpackHeader: swap_file_sz=" << |
576 | entry->swap_file_sz << "( " << swap_hdr_sz << " + " << | |
577 | entry->mem_obj->object_sz << ")"); | |
528b2c61 | 578 | } |
579 | ||
580 | void | |
581 | store_client::readHeader(char const *buf, ssize_t len) | |
582 | { | |
583 | MemObject *const mem = entry->mem_obj; | |
62e76326 | 584 | |
528b2c61 | 585 | assert(flags.disk_io_pending); |
3dd52a0b | 586 | flags.disk_io_pending = false; |
90703668 | 587 | assert(_callback.pending()); |
528b2c61 | 588 | |
589 | unpackHeader (buf, len); | |
62e76326 | 590 | |
528b2c61 | 591 | if (!object_ok) |
62e76326 | 592 | return; |
593 | ||
e3ef2b09 | 594 | /* |
595 | * If our last read got some data the client wants, then give | |
596 | * it to them, otherwise schedule another read. | |
597 | */ | |
528b2c61 | 598 | size_t body_sz = len - mem->swap_hdr_sz; |
62e76326 | 599 | |
47f6e231 | 600 | if (copyInto.offset < static_cast<int64_t>(body_sz)) { |
62e76326 | 601 | /* |
602 | * we have (part of) what they want | |
603 | */ | |
d85c3078 | 604 | size_t copy_sz = min(copyInto.length, body_sz); |
4a7a3d56 | 605 | debugs(90, 3, "storeClientReadHeader: copying " << copy_sz << " bytes of body"); |
41d00cd3 | 606 | memmove(copyInto.data, copyInto.data + mem->swap_hdr_sz, copy_sz); |
62e76326 | 607 | |
3196f526 | 608 | readBody(copyInto.data, copy_sz); |
528b2c61 | 609 | |
62e76326 | 610 | return; |
e3ef2b09 | 611 | } |
62e76326 | 612 | |
e3ef2b09 | 613 | /* |
614 | * we don't have what the client wants, but at least we now | |
615 | * know the swap header size. | |
616 | */ | |
528b2c61 | 617 | fileRead(); |
e3ef2b09 | 618 | } |
619 | ||
f09f5b26 | 620 | int |
a4b8110e | 621 | storeClientCopyPending(store_client * sc, StoreEntry * e, void *data) |
f09f5b26 | 622 | { |
06d2839d | 623 | #if STORE_CLIENT_LIST_DEBUG |
624 | assert(sc == storeClientListSearch(e->mem_obj, data)); | |
edce4d98 | 625 | #endif |
626 | #ifndef SILLY_CODE | |
62e76326 | 627 | |
edce4d98 | 628 | assert(sc); |
06d2839d | 629 | #endif |
62e76326 | 630 | |
06d2839d | 631 | assert(sc->entry == e); |
edce4d98 | 632 | #if SILLY_CODE |
62e76326 | 633 | |
f09f5b26 | 634 | if (sc == NULL) |
62e76326 | 635 | return 0; |
636 | ||
edce4d98 | 637 | #endif |
62e76326 | 638 | |
90703668 | 639 | if (!sc->_callback.pending()) |
62e76326 | 640 | return 0; |
641 | ||
f09f5b26 | 642 | return 1; |
643 | } | |
644 | ||
06d2839d | 645 | /* |
646 | * This routine hasn't been optimised to take advantage of the | |
647 | * passed sc. Yet. | |
648 | */ | |
f09f5b26 | 649 | int |
a4b8110e | 650 | storeUnregister(store_client * sc, StoreEntry * e, void *data) |
f09f5b26 | 651 | { |
652 | MemObject *mem = e->mem_obj; | |
06d2839d | 653 | #if STORE_CLIENT_LIST_DEBUG |
62e76326 | 654 | |
06d2839d | 655 | assert(sc == storeClientListSearch(e->mem_obj, data)); |
656 | #endif | |
62e76326 | 657 | |
f09f5b26 | 658 | if (mem == NULL) |
62e76326 | 659 | return 0; |
660 | ||
26ac0430 | 661 | debugs(90, 3, "storeUnregister: called for '" << e->getMD5Text() << "'"); |
62e76326 | 662 | |
2f44bd34 | 663 | if (sc == NULL) { |
bf8fe701 | 664 | debugs(90, 3, "storeUnregister: No matching client for '" << e->getMD5Text() << "'"); |
62e76326 | 665 | return 0; |
2f44bd34 | 666 | } |
62e76326 | 667 | |
0e3f3e0d | 668 | if (mem->clientCount() == 0) { |
bf8fe701 | 669 | debugs(90, 3, "storeUnregister: Consistency failure - store client being unregistered is not in the mem object's list for '" << e->getMD5Text() << "'"); |
62e76326 | 670 | return 0; |
2f44bd34 | 671 | } |
62e76326 | 672 | |
06d2839d | 673 | dlinkDelete(&sc->node, &mem->clients); |
5e263176 | 674 | -- mem->nclients; |
62e76326 | 675 | |
f09f5b26 | 676 | if (e->store_status == STORE_OK && e->swap_status != SWAPOUT_DONE) |
c07cbbf4 | 677 | e->swapOut(); |
62e76326 | 678 | |
85a4b153 | 679 | if (sc->swapin_sio != NULL) { |
aa1a691e | 680 | storeClose(sc->swapin_sio, StoreIOState::readerDone); |
62e76326 | 681 | sc->swapin_sio = NULL; |
5db6bf73 | 682 | ++statCounter.swap.ins; |
eb824054 | 683 | } |
62e76326 | 684 | |
90703668 | 685 | if (sc->_callback.pending()) { |
62e76326 | 686 | /* callback with ssize = -1 to indicate unexpected termination */ |
c877c0bc | 687 | debugs(90, 3, "store_client for " << *e << " has a callback"); |
62e76326 | 688 | sc->fail(); |
f09f5b26 | 689 | } |
62e76326 | 690 | |
fa80a8ef | 691 | #if STORE_CLIENT_LIST_DEBUG |
692 | cbdataReferenceDone(sc->owner); | |
62e76326 | 693 | |
fa80a8ef | 694 | #endif |
62e76326 | 695 | |
528b2c61 | 696 | delete sc; |
62e76326 | 697 | |
1bfe9ade | 698 | assert(e->locked()); |
178c7e33 AR |
699 | // An entry locked by others may be unlocked (and destructed) by others, so |
700 | // we must lock again to safely dereference e after CheckQuickAbort(). | |
701 | e->lock("storeUnregister"); | |
62e76326 | 702 | |
77b32a34 | 703 | if (mem->nclients == 0) |
62e76326 | 704 | CheckQuickAbort(e); |
a46d2c0e | 705 | else |
706 | mem->kickReads(); | |
62e76326 | 707 | |
0ad2b63b CT |
708 | #if USE_ADAPTATION |
709 | e->kickProducer(); | |
710 | #endif | |
711 | ||
178c7e33 | 712 | e->unlock("storeUnregister"); |
f09f5b26 | 713 | return 1; |
714 | } | |
715 | ||
f09f5b26 | 716 | /* Call handlers waiting for data to be appended to E. */ |
717 | void | |
d88e3c49 | 718 | StoreEntry::invokeHandlers() |
f09f5b26 | 719 | { |
528b2c61 | 720 | /* Commit what we can to disk, if appropriate */ |
d88e3c49 | 721 | swapOut(); |
f09f5b26 | 722 | int i = 0; |
f09f5b26 | 723 | store_client *sc; |
06d2839d | 724 | dlink_node *nx = NULL; |
725 | dlink_node *node; | |
726 | ||
0bc8db64 | 727 | PROF_start(InvokeHandlers); |
9ea37c79 | 728 | |
bf8fe701 | 729 | debugs(90, 3, "InvokeHandlers: " << getMD5Text() ); |
f09f5b26 | 730 | /* walk the entire list looking for valid callbacks */ |
62e76326 | 731 | |
d88e3c49 | 732 | for (node = mem_obj->clients.head; node; node = nx) { |
62e76326 | 733 | sc = (store_client *)node->data; |
734 | nx = node->next; | |
5db6bf73 FC |
735 | debugs(90, 3, "StoreEntry::InvokeHandlers: checking client #" << i ); |
736 | ++i; | |
62e76326 | 737 | |
90703668 | 738 | if (!sc->_callback.pending()) |
62e76326 | 739 | continue; |
740 | ||
741 | if (sc->flags.disk_io_pending) | |
742 | continue; | |
743 | ||
d88e3c49 | 744 | storeClientCopy2(this, sc); |
f09f5b26 | 745 | } |
0bc8db64 | 746 | PROF_stop(InvokeHandlers); |
f09f5b26 | 747 | } |
748 | ||
22bbd840 | 749 | // Does not account for remote readers/clients. |
f09f5b26 | 750 | int |
751 | storePendingNClients(const StoreEntry * e) | |
752 | { | |
f09f5b26 | 753 | MemObject *mem = e->mem_obj; |
36547bcf | 754 | int npend = NULL == mem ? 0 : mem->nclients; |
bf8fe701 | 755 | debugs(90, 3, "storePendingNClients: returning " << npend); |
f09f5b26 | 756 | return npend; |
757 | } | |
77b32a34 | 758 | |
ae6568e7 AJ |
759 | /* return true if the request should be aborted */ |
760 | static bool | |
761 | CheckQuickAbortIsReasonable(StoreEntry * entry) | |
77b32a34 | 762 | { |
528b2c61 | 763 | MemObject * const mem = entry->mem_obj; |
77b32a34 | 764 | assert(mem); |
ae6568e7 | 765 | debugs(90, 3, "entry=" << entry << ", mem=" << mem); |
62e76326 | 766 | |
45e5102d | 767 | if (mem->request && !mem->request->flags.cachable) { |
ae6568e7 AJ |
768 | debugs(90, 3, "quick-abort? YES !mem->request->flags.cachable"); |
769 | return true; | |
77b32a34 | 770 | } |
62e76326 | 771 | |
77b32a34 | 772 | if (EBIT_TEST(entry->flags, KEY_PRIVATE)) { |
ae6568e7 AJ |
773 | debugs(90, 3, "quick-abort? YES KEY_PRIVATE"); |
774 | return true; | |
77b32a34 | 775 | } |
62e76326 | 776 | |
47f6e231 | 777 | int64_t expectlen = entry->getReply()->content_length + entry->getReply()->hdr_sz; |
0e3f3e0d | 778 | |
ae6568e7 | 779 | if (expectlen < 0) { |
2324cda2 | 780 | /* expectlen is < 0 if *no* information about the object has been received */ |
ae6568e7 AJ |
781 | debugs(90, 3, "quick-abort? YES no object data received yet"); |
782 | return true; | |
783 | } | |
0e3f3e0d | 784 | |
ae6568e7 | 785 | int64_t curlen = mem->endOffset(); |
62e76326 | 786 | |
47f6e231 | 787 | if (Config.quickAbort.min < 0) { |
ae6568e7 AJ |
788 | debugs(90, 3, "quick-abort? NO disabled"); |
789 | return false; | |
77b32a34 | 790 | } |
62e76326 | 791 | |
ae6568e7 | 792 | if (mem->request && mem->request->range && mem->request->getRangeOffsetLimit() < 0) { |
ab275c7b | 793 | /* Don't abort if the admin has configured range_ofset -1 to download fully for caching. */ |
ae6568e7 AJ |
794 | debugs(90, 3, "quick-abort? NO admin configured range replies to full-download"); |
795 | return false; | |
ab275c7b AJ |
796 | } |
797 | ||
77b32a34 | 798 | if (curlen > expectlen) { |
f22b0af0 | 799 | debugs(90, 3, "quick-abort? YES bad content length (" << curlen << " of " << expectlen << " bytes received)"); |
ae6568e7 | 800 | return true; |
77b32a34 | 801 | } |
62e76326 | 802 | |
47f6e231 | 803 | if ((expectlen - curlen) < (Config.quickAbort.min << 10)) { |
ae6568e7 AJ |
804 | debugs(90, 3, "quick-abort? NO only a little more object left to receive"); |
805 | return false; | |
77b32a34 | 806 | } |
62e76326 | 807 | |
77b32a34 | 808 | if ((expectlen - curlen) > (Config.quickAbort.max << 10)) { |
ae6568e7 AJ |
809 | debugs(90, 3, "quick-abort? YES too much left to go"); |
810 | return true; | |
77b32a34 | 811 | } |
62e76326 | 812 | |
77b32a34 | 813 | if (expectlen < 100) { |
ae6568e7 AJ |
814 | debugs(90, 3, "quick-abort? NO avoid FPE"); |
815 | return false; | |
77b32a34 | 816 | } |
62e76326 | 817 | |
47f6e231 | 818 | if ((curlen / (expectlen / 100)) > (Config.quickAbort.pct)) { |
ae6568e7 AJ |
819 | debugs(90, 3, "quick-abort? NO past point of no return"); |
820 | return false; | |
77b32a34 | 821 | } |
62e76326 | 822 | |
ae6568e7 AJ |
823 | debugs(90, 3, "quick-abort? YES default"); |
824 | return true; | |
77b32a34 | 825 | } |
826 | ||
22bbd840 AR |
827 | /// Aborts a swapping-out entry if nobody needs it any more _and_ |
828 | /// continuing swap out is not reasonable per CheckQuickAbortIsReasonable(). | |
77b32a34 | 829 | static void |
830 | CheckQuickAbort(StoreEntry * entry) | |
831 | { | |
528b2c61 | 832 | assert (entry); |
62e76326 | 833 | |
77b32a34 | 834 | if (storePendingNClients(entry) > 0) |
62e76326 | 835 | return; |
836 | ||
22bbd840 AR |
837 | if (!shutting_down && Store::Root().transientReaders(*entry)) |
838 | return; | |
839 | ||
77b32a34 | 840 | if (entry->store_status != STORE_PENDING) |
62e76326 | 841 | return; |
842 | ||
986ebffc | 843 | if (EBIT_TEST(entry->flags, ENTRY_SPECIAL)) |
62e76326 | 844 | return; |
845 | ||
ae6568e7 | 846 | if (!CheckQuickAbortIsReasonable(entry)) |
62e76326 | 847 | return; |
848 | ||
bfb55b6f | 849 | entry->abort(); |
77b32a34 | 850 | } |
c8be6d7b | 851 | |
852 | void | |
fcc35180 | 853 | store_client::dumpStats(MemBuf * output, int clientNumber) const |
c8be6d7b | 854 | { |
90703668 | 855 | if (_callback.pending()) |
62e76326 | 856 | return; |
857 | ||
2fe7eff9 | 858 | output->Printf("\tClient #%d, %p\n", clientNumber, _callback.callback_data); |
62e76326 | 859 | |
c91ca3ce | 860 | output->Printf("\t\tcopy_offset: %" PRId64 "\n", |
47f6e231 | 861 | copyInto.offset); |
62e76326 | 862 | |
2fe7eff9 | 863 | output->Printf("\t\tcopy_size: %d\n", |
864 | (int) copyInto.length); | |
62e76326 | 865 | |
2fe7eff9 | 866 | output->Printf("\t\tflags:"); |
62e76326 | 867 | |
528b2c61 | 868 | if (flags.disk_io_pending) |
2fe7eff9 | 869 | output->Printf(" disk_io_pending"); |
62e76326 | 870 | |
528b2c61 | 871 | if (flags.store_copying) |
2fe7eff9 | 872 | output->Printf(" store_copying"); |
62e76326 | 873 | |
528b2c61 | 874 | if (flags.copy_event_pending) |
2fe7eff9 | 875 | output->Printf(" copy_event_pending"); |
62e76326 | 876 | |
2fe7eff9 | 877 | output->Printf("\n"); |
528b2c61 | 878 | } |
c8be6d7b | 879 | |
528b2c61 | 880 | bool |
90703668 | 881 | store_client::Callback::pending() const |
528b2c61 | 882 | { |
90703668 | 883 | return callback_handler && callback_data; |
c8be6d7b | 884 | } |
528b2c61 | 885 | |
886 | store_client::Callback::Callback(STCB *function, void *data) : callback_handler(function), callback_data (data) {} | |
b67e2c8c | 887 | |
9a0a18de | 888 | #if USE_DELAY_POOLS |
b67e2c8c | 889 | void |
890 | store_client::setDelayId(DelayId delay_id) | |
891 | { | |
892 | delayId = delay_id; | |
893 | } | |
515ec4dc | 894 | #endif |
f53969cc | 895 |