]>
Commit | Line | Data |
---|---|---|
9cef6668 | 1 | /* |
b8ae064d | 2 | * Copyright (C) 1996-2023 The Squid Software Foundation and contributors |
9cef6668 | 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 20 Storage Manager Swapout Functions */ |
10 | ||
582c2af2 | 11 | #include "squid.h" |
a4dd5bfa | 12 | #include "base/IoManip.h" |
aa839030 | 13 | #include "cbdata.h" |
4310f8b0 | 14 | #include "CollapsedForwarding.h" |
af69c635 | 15 | #include "globals.h" |
e6ccf245 | 16 | #include "Store.h" |
602d9612 | 17 | #include "StoreClient.h" |
9837567d | 18 | // TODO: Abstract the use of this more |
528b2c61 | 19 | #include "mem_node.h" |
20 | #include "MemObject.h" | |
4d5904f7 | 21 | #include "SquidConfig.h" |
e4f1fdae | 22 | #include "StatCounters.h" |
2745fea5 AR |
23 | #include "store/Disk.h" |
24 | #include "store/Disks.h" | |
b3f7fd88 AR |
25 | #include "store_log.h" |
26 | #include "swap_log_op.h" | |
f09f5b26 | 27 | |
d54f0ab3 | 28 | static void storeSwapOutStart(StoreEntry * e); |
4fcc8876 | 29 | static StoreIOState::STIOCB storeSwapOutFileClosed; |
f09f5b26 | 30 | |
daacd51f AJ |
31 | // wrapper to cross C/C++ ABI boundary. xfree is extern "C" for libraries. |
32 | static void xfree_cppwrapper(void *x) | |
33 | { | |
34 | xfree(x); | |
35 | } | |
36 | ||
f09f5b26 | 37 | /* start swapping object to disk */ |
d54f0ab3 | 38 | static void |
f09f5b26 | 39 | storeSwapOutStart(StoreEntry * e) |
40 | { | |
2391a162 | 41 | MemObject *mem = e->mem_obj; |
d3b3ab85 | 42 | StoreIOState::Pointer sio; |
2391a162 | 43 | assert(mem); |
cd748f27 | 44 | /* Build the swap metadata, so the filesystem will know how much |
45 | * metadata there is to store | |
46 | */ | |
26ac0430 | 47 | debugs(20, 5, "storeSwapOutStart: Begin SwapOut '" << e->url() << "' to dirno " << |
a4dd5bfa | 48 | e->swap_dirn << ", fileno " << asHex(e->swap_filen).upperCase().minDigits(8)); |
528b2c61 | 49 | /* If we start swapping out objects with OutOfBand Metadata, |
50 | * then this code needs changing | |
51 | */ | |
246e6cc1 AJ |
52 | |
53 | /* TODO: make some sort of data,size refcounted immutable buffer | |
54 | * and stop fooling ourselves with "const char*" buffers. | |
55 | */ | |
56 | ||
57 | // Create metadata now, possibly in vain: storeCreate needs swap_hdr_sz. | |
66d51f4f | 58 | const auto buf = e->getSerialisedMetaData(mem->swap_hdr_sz); |
246e6cc1 AJ |
59 | assert(buf); |
60 | ||
cd748f27 | 61 | /* Create the swap file */ |
aa839030 | 62 | generic_cbdata *c = new generic_cbdata(e); |
be42c788 | 63 | sio = storeCreate(e, storeSwapOutFileClosed, c); |
62e76326 | 64 | |
aee3523a | 65 | if (sio == nullptr) { |
4310f8b0 | 66 | assert(!e->hasDisk()); |
62e76326 | 67 | e->swap_status = SWAPOUT_NONE; |
0cdcf3d7 | 68 | e->swapOutDecision(MemObject::SwapOut::swImpossible); |
aa839030 | 69 | delete c; |
246e6cc1 | 70 | xfree((char*)buf); |
62e76326 | 71 | storeLog(STORE_LOG_SWAPOUTFAIL, e); |
72 | return; | |
3fc29499 | 73 | } |
62e76326 | 74 | |
528b2c61 | 75 | mem->swapout.sio = sio; |
76 | /* Don't lock until after create, or the replacement | |
77 | * code might get confused */ | |
34266cde | 78 | |
1bfe9ade | 79 | e->lock("storeSwapOutStart"); |
cd748f27 | 80 | /* Pick up the file number if it was assigned immediately */ |
4310f8b0 | 81 | e->attachToDisk(mem->swapout.sio->swap_dirn, mem->swapout.sio->swap_filen, SWAPOUT_WRITING); |
34266cde | 82 | |
24c93780 AR |
83 | e->swapOutDecision(MemObject::SwapOut::swStarted); // after SWAPOUT_WRITING |
84 | ||
cd748f27 | 85 | /* write out the swap metadata */ |
daacd51f | 86 | storeIOWrite(mem->swapout.sio, buf, mem->swap_hdr_sz, 0, xfree_cppwrapper); |
f09f5b26 | 87 | } |
88 | ||
90e8b325 | 89 | static bool |
528b2c61 | 90 | doPages(StoreEntry *anEntry) |
91 | { | |
92 | MemObject *mem = anEntry->mem_obj; | |
62e76326 | 93 | |
528b2c61 | 94 | do { |
aa1a691e AR |
95 | // find the page containing the first byte we have not swapped out yet |
96 | mem_node *page = | |
97 | mem->data_hdr.getBlockContainingLocation(mem->swapout.queue_offset); | |
62e76326 | 98 | |
aa1a691e | 99 | if (!page) |
90e8b325 | 100 | break; // wait for more data to become available |
aa1a691e AR |
101 | |
102 | // memNodeWriteComplete() and absence of buffer offset math below | |
103 | // imply that we always write from the very beginning of the page | |
104 | assert(page->start() == mem->swapout.queue_offset); | |
62e76326 | 105 | |
106 | /* | |
107 | * Get the length of this buffer. We are assuming(!) that the buffer | |
108 | * length won't change on this buffer, or things are going to be very | |
109 | * strange. I think that after the copy to a buffer is done, the buffer | |
110 | * size should stay fixed regardless so that this code isn't confused, | |
111 | * but we can look at this at a later date or whenever the code results | |
112 | * in bad swapouts, whichever happens first. :-) | |
113 | */ | |
aa1a691e | 114 | ssize_t swap_buf_len = page->nodeBuffer.length; |
62e76326 | 115 | |
4a7a3d56 | 116 | debugs(20, 3, "storeSwapOut: swap_buf_len = " << swap_buf_len); |
62e76326 | 117 | |
118 | assert(swap_buf_len > 0); | |
119 | ||
4a7a3d56 | 120 | debugs(20, 3, "storeSwapOut: swapping out " << swap_buf_len << " bytes from " << mem->swapout.queue_offset); |
62e76326 | 121 | |
122 | mem->swapout.queue_offset += swap_buf_len; | |
123 | ||
90e8b325 AR |
124 | // Quit if write() fails. Sio is going to call our callback, and that |
125 | // will cleanup, but, depending on the fs, that call may be async. | |
126 | const bool ok = mem->swapout.sio->write( | |
9d4e9cfb AR |
127 | mem->data_hdr.NodeGet(page), |
128 | swap_buf_len, | |
129 | -1, | |
130 | memNodeWriteComplete); | |
62e76326 | 131 | |
4310f8b0 | 132 | if (!ok || !anEntry->swappingOut()) |
90e8b325 | 133 | return false; |
62e76326 | 134 | |
47f6e231 | 135 | int64_t swapout_size = mem->endOffset() - mem->swapout.queue_offset; |
62e76326 | 136 | |
137 | if (anEntry->store_status == STORE_PENDING) | |
138 | if (swapout_size < SM_PAGE_SIZE) | |
139 | break; | |
140 | ||
141 | if (swapout_size <= 0) | |
90e8b325 | 142 | break; |
528b2c61 | 143 | } while (true); |
90e8b325 AR |
144 | |
145 | // either wait for more data or call swapOutFileClose() | |
146 | return true; | |
528b2c61 | 147 | } |
148 | ||
528b2c61 | 149 | /* This routine is called every time data is sent to the client side. |
150 | * It's overhead is therefor, significant. | |
151 | */ | |
f09f5b26 | 152 | void |
c07cbbf4 | 153 | StoreEntry::swapOut() |
f09f5b26 | 154 | { |
c07cbbf4 | 155 | if (!mem_obj) |
62e76326 | 156 | return; |
157 | ||
5b55f1f1 CT |
158 | // this flag may change so we must check even if we are swappingOut |
159 | if (EBIT_TEST(flags, ENTRY_ABORTED)) { | |
160 | assert(EBIT_TEST(flags, RELEASE_REQUEST)); | |
161 | // StoreEntry::abort() already closed the swap out file, if any | |
162 | // no trimming: data producer must stop production if ENTRY_ABORTED | |
62e76326 | 163 | return; |
5b55f1f1 CT |
164 | } |
165 | ||
166 | const bool weAreOrMayBeSwappingOut = swappingOut() || mayStartSwapOut(); | |
55bfc9fc | 167 | |
4475555f | 168 | Store::Root().memoryOut(*this, weAreOrMayBeSwappingOut); |
5b55f1f1 | 169 | |
2d4dea47 | 170 | if (mem_obj->swapout.decision < MemObject::SwapOut::swPossible) |
fa22b512 AR |
171 | return; // decided not to write to disk (at least for now) |
172 | ||
173 | if (!weAreOrMayBeSwappingOut) | |
174 | return; // finished writing to disk after an earlier swStarted decision | |
528b2c61 | 175 | |
aa1a691e AR |
176 | // Aborted entries have STORE_OK, but swapoutPossible rejects them. Thus, |
177 | // store_status == STORE_OK below means we got everything we wanted. | |
178 | ||
bf95c10a | 179 | debugs(20, 7, "storeSwapOut: mem->inmem_lo = " << mem_obj->inmem_lo); |
180 | debugs(20, 7, "storeSwapOut: mem->endOffset() = " << mem_obj->endOffset()); | |
181 | debugs(20, 7, "storeSwapOut: swapout.queue_offset = " << mem_obj->swapout.queue_offset); | |
62e76326 | 182 | |
aee3523a | 183 | if (mem_obj->swapout.sio != nullptr) |
26ac0430 | 184 | debugs(20, 7, "storeSwapOut: storeOffset() = " << mem_obj->swapout.sio->offset() ); |
62e76326 | 185 | |
47f6e231 | 186 | int64_t const lowest_offset = mem_obj->lowestMemReaderOffset(); |
62e76326 | 187 | |
bf95c10a | 188 | debugs(20, 7, "storeSwapOut: lowest_offset = " << lowest_offset); |
62e76326 | 189 | |
5aecb102 | 190 | #if SIZEOF_OFF_T <= 4 |
62e76326 | 191 | |
c07cbbf4 | 192 | if (mem_obj->endOffset() > 0x7FFF0000) { |
fa84c01d | 193 | debugs(20, DBG_CRITICAL, "WARNING: preventing off_t overflow for " << url()); |
bfb55b6f | 194 | abort(); |
62e76326 | 195 | return; |
adcceb47 | 196 | } |
62e76326 | 197 | |
adcceb47 | 198 | #endif |
4310f8b0 | 199 | if (swappingOut()) |
47f6e231 | 200 | assert(mem_obj->inmem_lo <= mem_obj->objectBytesOnDisk() ); |
62e76326 | 201 | |
5b55f1f1 CT |
202 | // buffered bytes we have not swapped out yet |
203 | const int64_t swapout_maxsize = mem_obj->availableForSwapOut(); | |
204 | assert(swapout_maxsize >= 0); | |
4a7a3d56 | 205 | debugs(20, 7, "storeSwapOut: swapout_size = " << swapout_maxsize); |
62e76326 | 206 | |
aa1a691e AR |
207 | if (swapout_maxsize == 0) { // swapped everything we got |
208 | if (store_status == STORE_OK) { // got everything we wanted | |
209 | assert(mem_obj->object_sz >= 0); | |
210 | swapOutFileClose(StoreIOState::wroteAll); | |
211 | } | |
212 | // else need more data to swap out | |
213 | return; | |
1b72bda1 | 214 | } |
62e76326 | 215 | |
c07cbbf4 | 216 | if (store_status == STORE_PENDING) { |
62e76326 | 217 | /* wait for a full block to write */ |
218 | ||
219 | if (swapout_maxsize < SM_PAGE_SIZE) | |
220 | return; | |
221 | ||
222 | /* | |
223 | * Wait until we are below the disk FD limit, only if the | |
d5430dc8 | 224 | * next read won't be deferred. |
62e76326 | 225 | */ |
c07cbbf4 | 226 | if (storeTooManyDiskFilesOpen() && !checkDeferRead(-1)) |
62e76326 | 227 | return; |
c47511fd | 228 | } |
62e76326 | 229 | |
2391a162 | 230 | /* Ok, we have stuff to swap out. Is there a swapout.sio open? */ |
4310f8b0 | 231 | if (!hasDisk()) { |
aee3523a | 232 | assert(mem_obj->swapout.sio == nullptr); |
c07cbbf4 | 233 | assert(mem_obj->inmem_lo == 0); |
ddc9b32c | 234 | storeSwapOutStart(this); // sets SwapOut::swImpossible on failures |
f09f5b26 | 235 | } |
62e76326 | 236 | |
aee3523a | 237 | if (mem_obj->swapout.sio == nullptr) |
62e76326 | 238 | return; |
239 | ||
90e8b325 | 240 | if (!doPages(this)) |
62e76326 | 241 | /* oops, we're not swapping out any more */ |
242 | return; | |
243 | ||
c07cbbf4 | 244 | if (store_status == STORE_OK) { |
62e76326 | 245 | /* |
246 | * If the state is STORE_OK, then all data must have been given | |
247 | * to the filesystem at this point because storeSwapOut() is | |
248 | * not going to be called again for this entry. | |
249 | */ | |
aa1a691e | 250 | assert(mem_obj->object_sz >= 0); |
c07cbbf4 | 251 | assert(mem_obj->endOffset() == mem_obj->swapout.queue_offset); |
aa1a691e | 252 | swapOutFileClose(StoreIOState::wroteAll); |
1792fbdb | 253 | } |
f09f5b26 | 254 | } |
255 | ||
256 | void | |
aa1a691e | 257 | StoreEntry::swapOutFileClose(int how) |
f09f5b26 | 258 | { |
aee3523a | 259 | assert(mem_obj != nullptr); |
aa1a691e | 260 | debugs(20, 3, "storeSwapOutFileClose: " << getMD5Text() << " how=" << how); |
bf8fe701 | 261 | debugs(20, 3, "storeSwapOutFileClose: sio = " << mem_obj->swapout.sio.getRaw()); |
62e76326 | 262 | |
aee3523a | 263 | if (mem_obj->swapout.sio == nullptr) |
62e76326 | 264 | return; |
265 | ||
aa1a691e | 266 | storeClose(mem_obj->swapout.sio, how); |
f09f5b26 | 267 | } |
268 | ||
269 | static void | |
e5de8b13 | 270 | storeSwapOutFileClosed(void *data, int errflag, StoreIOState::Pointer self) |
f09f5b26 | 271 | { |
bd6e2f16 AJ |
272 | StoreEntry *e; |
273 | static_cast<generic_cbdata *>(data)->unwrap(&e); | |
274 | ||
25354045 | 275 | MemObject *mem = e->mem_obj; |
84177444 | 276 | assert(mem->swapout.sio == self); |
4310f8b0 | 277 | assert(e->swappingOut()); |
62e76326 | 278 | |
aa1a691e AR |
279 | // if object_size is still unknown, the entry was probably aborted |
280 | if (errflag || e->objectLen() < 0) { | |
de4bc0ea | 281 | debugs(20, 2, "storeSwapOutFileClosed: dirno " << e->swap_dirn << ", swapfile " << |
a4dd5bfa FC |
282 | asHex(e->swap_filen).upperCase().minDigits(8) << |
283 | ", errflag=" << errflag); | |
62e76326 | 284 | |
285 | if (errflag == DISK_NO_SPACE_LEFT) { | |
9837567d | 286 | /* TODO: this should be handle by the link from store IO to |
c8f4eac4 | 287 | * Store, rather than being a top level API call. |
288 | */ | |
2745fea5 | 289 | e->disk().diskFull(); |
62e76326 | 290 | storeConfigure(); |
291 | } | |
292 | ||
02ba667b EB |
293 | // mark the locked entry for deletion |
294 | // TODO: Keep the memory entry (if any) | |
295 | e->releaseRequest(); | |
296 | e->swap_status = SWAPOUT_FAILED; | |
4310f8b0 | 297 | e->disk().finalizeSwapoutFailure(*e); |
2391a162 | 298 | } else { |
62e76326 | 299 | /* swapping complete */ |
26ac0430 | 300 | debugs(20, 3, "storeSwapOutFileClosed: SwapOut complete: '" << e->url() << "' to " << |
a4dd5bfa | 301 | e->swap_dirn << ", " << asHex(e->swap_filen).upperCase().minDigits(8)); |
bf95c10a | 302 | debugs(20, 5, "swap_file_sz = " << |
aa1a691e | 303 | e->objectLen() << " + " << mem->swap_hdr_sz); |
8061c9b2 | 304 | |
707fdc47 | 305 | e->swap_file_sz = e->objectLen() + mem->swap_hdr_sz; |
62e76326 | 306 | e->swap_status = SWAPOUT_DONE; |
4310f8b0 | 307 | e->disk().finalizeSwapoutSuccess(*e); |
62e76326 | 308 | |
ddc9b32c AR |
309 | // XXX: For some Stores, it is pointless to re-check cachability here |
310 | // and it leads to double counts in store_check_cachable_hist. We need | |
311 | // another way to signal a completed but failed swapout. Or, better, | |
312 | // each Store should handle its own logging and LOG state setting. | |
3900307b | 313 | if (e->checkCachable()) { |
62e76326 | 314 | storeLog(STORE_LOG_SWAPOUT, e); |
315 | storeDirSwapLog(e, SWAP_LOG_ADD); | |
316 | } | |
317 | ||
e4f1fdae | 318 | ++statCounter.swap.outs; |
f09f5b26 | 319 | } |
62e76326 | 320 | |
bf8fe701 | 321 | debugs(20, 3, "storeSwapOutFileClosed: " << __FILE__ << ":" << __LINE__); |
aee3523a | 322 | mem->swapout.sio = nullptr; |
24c93780 | 323 | e->storeWriterDone(); // after updating swap_status |
1bfe9ade | 324 | e->unlock("storeSwapOutFileClosed"); |
f09f5b26 | 325 | } |
61038223 | 326 | |
c07cbbf4 | 327 | bool |
5b55f1f1 | 328 | StoreEntry::mayStartSwapOut() |
c2725718 | 329 | { |
5b55f1f1 CT |
330 | // must be checked in the caller |
331 | assert(!EBIT_TEST(flags, ENTRY_ABORTED)); | |
4094b311 | 332 | assert(!swappingOut()); |
5b55f1f1 CT |
333 | |
334 | if (!Config.cacheSwap.n_configured) | |
335 | return false; | |
336 | ||
337 | assert(mem_obj); | |
0cdcf3d7 | 338 | const MemObject::SwapOut::Decision &decision = mem_obj->swapout.decision; |
5b55f1f1 | 339 | |
0e3b2ff0 | 340 | // if we decided that starting is not possible, do not repeat same checks |
5b55f1f1 | 341 | if (decision == MemObject::SwapOut::swImpossible) { |
bf95c10a | 342 | debugs(20, 3, " already rejected"); |
5b55f1f1 CT |
343 | return false; |
344 | } | |
345 | ||
24c93780 AR |
346 | // If we have started swapping out, do not start over. Most likely, we have |
347 | // finished swapping out by now because we are not currently swappingOut(). | |
348 | if (decision == MemObject::SwapOut::swStarted) { | |
349 | debugs(20, 3, "already started"); | |
4094b311 | 350 | return false; |
5b55f1f1 CT |
351 | } |
352 | ||
23b79630 AR |
353 | if (shutting_down) { |
354 | debugs(20, 3, "avoid heavy optional work during shutdown"); | |
355 | swapOutDecision(MemObject::SwapOut::swImpossible); | |
356 | return false; | |
357 | } | |
358 | ||
24c93780 AR |
359 | // if there is a usable disk entry already, do not start over |
360 | if (hasDisk() || Store::Root().hasReadableDiskEntry(*this)) { | |
361 | debugs(20, 3, "already did"); // we or somebody else created that entry | |
0cdcf3d7 | 362 | swapOutDecision(MemObject::SwapOut::swImpossible); |
0e3b2ff0 AR |
363 | return false; |
364 | } | |
365 | ||
4310f8b0 EB |
366 | if (Store::Root().markedForDeletionAndAbandoned(*this)) { |
367 | debugs(20, 3, "marked for deletion and abandoned"); | |
368 | swapOutDecision(MemObject::SwapOut::swImpossible); | |
369 | return false; | |
370 | } | |
371 | ||
0e3b2ff0 AR |
372 | // if we decided that swapout is possible, do not repeat same checks |
373 | if (decision == MemObject::SwapOut::swPossible) { | |
374 | debugs(20, 3, "already allowed"); | |
375 | return true; | |
376 | } | |
377 | ||
24c93780 AR |
378 | // To avoid SMP workers releasing each other caching attempts, restrict disk |
379 | // caching to StoreEntry publisher. This check goes before checkCachable() | |
380 | // that may incorrectly release() publisher's entry. | |
381 | if (Store::Root().transientsReader(*this)) { | |
382 | debugs(20, 5, "yield to entry publisher"); | |
383 | swapOutDecision(MemObject::SwapOut::swImpossible); | |
384 | return false; | |
385 | } | |
386 | ||
5b55f1f1 | 387 | if (!checkCachable()) { |
bf95c10a | 388 | debugs(20, 3, "not cachable"); |
0cdcf3d7 | 389 | swapOutDecision(MemObject::SwapOut::swImpossible); |
5b55f1f1 CT |
390 | return false; |
391 | } | |
392 | ||
393 | if (EBIT_TEST(flags, ENTRY_SPECIAL)) { | |
bf95c10a | 394 | debugs(20, 3, url() << " SPECIAL"); |
0cdcf3d7 | 395 | swapOutDecision(MemObject::SwapOut::swImpossible); |
5b55f1f1 CT |
396 | return false; |
397 | } | |
398 | ||
e0397786 AR |
399 | if (mem_obj->inmem_lo > 0) { |
400 | debugs(20, 3, "storeSwapOut: (inmem_lo > 0) imem_lo:" << mem_obj->inmem_lo); | |
0cdcf3d7 | 401 | swapOutDecision(MemObject::SwapOut::swImpossible); |
e0397786 AR |
402 | return false; |
403 | } | |
404 | ||
405 | if (!mem_obj->isContiguous()) { | |
406 | debugs(20, 3, "storeSwapOut: not Contiguous"); | |
0cdcf3d7 | 407 | swapOutDecision(MemObject::SwapOut::swImpossible); |
e0397786 AR |
408 | return false; |
409 | } | |
410 | ||
3c856e95 AR |
411 | // handle store_maxobjsize limit |
412 | { | |
5b55f1f1 CT |
413 | // TODO: add estimated store metadata size to be conservative |
414 | ||
415 | // use guaranteed maximum if it is known | |
416 | const int64_t expectedEnd = mem_obj->expectedReplySize(); | |
bf95c10a | 417 | debugs(20, 7, "expectedEnd = " << expectedEnd); |
5b55f1f1 | 418 | if (expectedEnd > store_maxobjsize) { |
bf95c10a | 419 | debugs(20, 3, "will not fit: " << expectedEnd << |
5b55f1f1 | 420 | " > " << store_maxobjsize); |
0cdcf3d7 | 421 | swapOutDecision(MemObject::SwapOut::swImpossible); |
5b55f1f1 CT |
422 | return false; // known to outgrow the limit eventually |
423 | } | |
424 | ||
425 | // use current minimum (always known) | |
426 | const int64_t currentEnd = mem_obj->endOffset(); | |
427 | if (currentEnd > store_maxobjsize) { | |
bf95c10a | 428 | debugs(20, 3, "does not fit: " << currentEnd << |
5b55f1f1 | 429 | " > " << store_maxobjsize); |
0cdcf3d7 | 430 | swapOutDecision(MemObject::SwapOut::swImpossible); |
5b55f1f1 CT |
431 | return false; // already does not fit and may only get bigger |
432 | } | |
433 | ||
e0397786 AR |
434 | // prevent final default swPossible answer for yet unknown length |
435 | if (expectedEnd < 0 && store_status != STORE_OK) { | |
5ca027f0 AR |
436 | const int64_t more = Store::Root().accumulateMore(*this); |
437 | if (more > 0) { | |
438 | debugs(20, 5, "got " << currentEnd << "; defer decision for " << more << " more bytes"); | |
439 | return true; // may still fit, but no final decision yet | |
440 | } | |
5b55f1f1 | 441 | } |
06d2839d | 442 | } |
62e76326 | 443 | |
0cdcf3d7 | 444 | swapOutDecision(MemObject::SwapOut::swPossible); |
c07cbbf4 | 445 | return true; |
c2725718 | 446 | } |
f53969cc | 447 |