2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 20 Storage Manager Swapout Functions */
15 #include "StoreClient.h"
16 /* FIXME: Abstract the use of this more */
18 #include "MemObject.h"
19 #include "SquidConfig.h"
20 #include "StatCounters.h"
21 #include "store/Disk.h"
22 #include "store/Disks.h"
23 #include "store_log.h"
24 #include "swap_log_op.h"
26 static void storeSwapOutStart(StoreEntry
* e
);
27 static StoreIOState::STIOCB storeSwapOutFileClosed
;
28 static StoreIOState::STFNCB storeSwapOutFileNotify
;
30 // wrapper to cross C/C++ ABI boundary. xfree is extern "C" for libraries.
31 static void xfree_cppwrapper(void *x
)
36 /* start swapping object to disk */
38 storeSwapOutStart(StoreEntry
* e
)
40 MemObject
*mem
= e
->mem_obj
;
41 StoreIOState::Pointer sio
;
43 /* Build the swap metadata, so the filesystem will know how much
44 * metadata there is to store
46 debugs(20, 5, "storeSwapOutStart: Begin SwapOut '" << e
->url() << "' to dirno " <<
47 e
->swap_dirn
<< ", fileno " << std::hex
<< std::setw(8) << std::setfill('0') <<
48 std::uppercase
<< e
->swap_filen
);
49 e
->swap_status
= SWAPOUT_WRITING
;
50 e
->swapOutDecision(MemObject::SwapOut::swStarted
);
51 /* If we start swapping out objects with OutOfBand Metadata,
52 * then this code needs changing
55 /* TODO: make some sort of data,size refcounted immutable buffer
56 * and stop fooling ourselves with "const char*" buffers.
59 // Create metadata now, possibly in vain: storeCreate needs swap_hdr_sz.
60 const char *buf
= e
->getSerialisedMetaData ();
63 /* Create the swap file */
64 generic_cbdata
*c
= new generic_cbdata(e
);
65 sio
= storeCreate(e
, storeSwapOutFileNotify
, storeSwapOutFileClosed
, c
);
68 e
->swap_status
= SWAPOUT_NONE
;
69 e
->swapOutDecision(MemObject::SwapOut::swImpossible
);
72 storeLog(STORE_LOG_SWAPOUTFAIL
, e
);
76 mem
->swapout
.sio
= sio
;
77 /* Don't lock until after create, or the replacement
78 * code might get confused */
80 e
->lock("storeSwapOutStart");
81 /* Pick up the file number if it was assigned immediately */
82 e
->swap_filen
= mem
->swapout
.sio
->swap_filen
;
84 e
->swap_dirn
= mem
->swapout
.sio
->swap_dirn
;
86 /* write out the swap metadata */
87 storeIOWrite(mem
->swapout
.sio
, buf
, mem
->swap_hdr_sz
, 0, xfree_cppwrapper
);
91 storeSwapOutFileNotify(void *data
, int errflag
, StoreIOState::Pointer self
)
94 static_cast<generic_cbdata
*>(data
)->unwrap(&e
);
96 MemObject
*mem
= e
->mem_obj
;
97 assert(e
->swap_status
== SWAPOUT_WRITING
);
99 assert(mem
->swapout
.sio
== self
);
100 assert(errflag
== 0);
101 assert(e
->swap_filen
< 0); // if this fails, call SwapDir::disconnect(e)
102 e
->swap_filen
= mem
->swapout
.sio
->swap_filen
;
103 e
->swap_dirn
= mem
->swapout
.sio
->swap_dirn
;
107 doPages(StoreEntry
*anEntry
)
109 MemObject
*mem
= anEntry
->mem_obj
;
112 // find the page containing the first byte we have not swapped out yet
114 mem
->data_hdr
.getBlockContainingLocation(mem
->swapout
.queue_offset
);
117 break; // wait for more data to become available
119 // memNodeWriteComplete() and absence of buffer offset math below
120 // imply that we always write from the very beginning of the page
121 assert(page
->start() == mem
->swapout
.queue_offset
);
124 * Get the length of this buffer. We are assuming(!) that the buffer
125 * length won't change on this buffer, or things are going to be very
126 * strange. I think that after the copy to a buffer is done, the buffer
127 * size should stay fixed regardless so that this code isn't confused,
128 * but we can look at this at a later date or whenever the code results
129 * in bad swapouts, whichever happens first. :-)
131 ssize_t swap_buf_len
= page
->nodeBuffer
.length
;
133 debugs(20, 3, "storeSwapOut: swap_buf_len = " << swap_buf_len
);
135 assert(swap_buf_len
> 0);
137 debugs(20, 3, "storeSwapOut: swapping out " << swap_buf_len
<< " bytes from " << mem
->swapout
.queue_offset
);
139 mem
->swapout
.queue_offset
+= swap_buf_len
;
141 // Quit if write() fails. Sio is going to call our callback, and that
142 // will cleanup, but, depending on the fs, that call may be async.
143 const bool ok
= mem
->swapout
.sio
->write(
144 mem
->data_hdr
.NodeGet(page
),
147 memNodeWriteComplete
);
149 if (!ok
|| anEntry
->swap_status
!= SWAPOUT_WRITING
)
152 int64_t swapout_size
= mem
->endOffset() - mem
->swapout
.queue_offset
;
154 if (anEntry
->store_status
== STORE_PENDING
)
155 if (swapout_size
< SM_PAGE_SIZE
)
158 if (swapout_size
<= 0)
162 // either wait for more data or call swapOutFileClose()
166 /* This routine is called every time data is sent to the client side.
167 * It's overhead is therefor, significant.
170 StoreEntry::swapOut()
175 // this flag may change so we must check even if we are swappingOut
176 if (EBIT_TEST(flags
, ENTRY_ABORTED
)) {
177 assert(EBIT_TEST(flags
, RELEASE_REQUEST
));
178 // StoreEntry::abort() already closed the swap out file, if any
179 // no trimming: data producer must stop production if ENTRY_ABORTED
183 const bool weAreOrMayBeSwappingOut
= swappingOut() || mayStartSwapOut();
185 Store::Root().memoryOut(*this, weAreOrMayBeSwappingOut
);
187 if (mem_obj
->swapout
.decision
< MemObject::SwapOut::swPossible
)
188 return; // nothing else to do
190 // Aborted entries have STORE_OK, but swapoutPossible rejects them. Thus,
191 // store_status == STORE_OK below means we got everything we wanted.
193 debugs(20, 7, HERE
<< "storeSwapOut: mem->inmem_lo = " << mem_obj
->inmem_lo
);
194 debugs(20, 7, HERE
<< "storeSwapOut: mem->endOffset() = " << mem_obj
->endOffset());
195 debugs(20, 7, HERE
<< "storeSwapOut: swapout.queue_offset = " << mem_obj
->swapout
.queue_offset
);
197 if (mem_obj
->swapout
.sio
!= NULL
)
198 debugs(20, 7, "storeSwapOut: storeOffset() = " << mem_obj
->swapout
.sio
->offset() );
200 int64_t const lowest_offset
= mem_obj
->lowestMemReaderOffset();
202 debugs(20, 7, HERE
<< "storeSwapOut: lowest_offset = " << lowest_offset
);
204 #if SIZEOF_OFF_T <= 4
206 if (mem_obj
->endOffset() > 0x7FFF0000) {
207 debugs(20, DBG_CRITICAL
, "WARNING: preventing off_t overflow for " << url());
213 if (swap_status
== SWAPOUT_WRITING
)
214 assert(mem_obj
->inmem_lo
<= mem_obj
->objectBytesOnDisk() );
216 // buffered bytes we have not swapped out yet
217 const int64_t swapout_maxsize
= mem_obj
->availableForSwapOut();
218 assert(swapout_maxsize
>= 0);
219 debugs(20, 7, "storeSwapOut: swapout_size = " << swapout_maxsize
);
221 if (swapout_maxsize
== 0) { // swapped everything we got
222 if (store_status
== STORE_OK
) { // got everything we wanted
223 assert(mem_obj
->object_sz
>= 0);
224 swapOutFileClose(StoreIOState::wroteAll
);
226 // else need more data to swap out
230 if (store_status
== STORE_PENDING
) {
231 /* wait for a full block to write */
233 if (swapout_maxsize
< SM_PAGE_SIZE
)
237 * Wait until we are below the disk FD limit, only if the
238 * next read won't be deferred.
240 if (storeTooManyDiskFilesOpen() && !checkDeferRead(-1))
244 /* Ok, we have stuff to swap out. Is there a swapout.sio open? */
245 if (swap_status
== SWAPOUT_NONE
) {
246 assert(mem_obj
->swapout
.sio
== NULL
);
247 assert(mem_obj
->inmem_lo
== 0);
248 storeSwapOutStart(this); // sets SwapOut::swImpossible on failures
251 if (mem_obj
->swapout
.sio
== NULL
)
255 /* oops, we're not swapping out any more */
258 if (store_status
== STORE_OK
) {
260 * If the state is STORE_OK, then all data must have been given
261 * to the filesystem at this point because storeSwapOut() is
262 * not going to be called again for this entry.
264 assert(mem_obj
->object_sz
>= 0);
265 assert(mem_obj
->endOffset() == mem_obj
->swapout
.queue_offset
);
266 swapOutFileClose(StoreIOState::wroteAll
);
271 StoreEntry::swapOutFileClose(int how
)
273 assert(mem_obj
!= NULL
);
274 debugs(20, 3, "storeSwapOutFileClose: " << getMD5Text() << " how=" << how
);
275 debugs(20, 3, "storeSwapOutFileClose: sio = " << mem_obj
->swapout
.sio
.getRaw());
277 if (mem_obj
->swapout
.sio
== NULL
)
280 storeClose(mem_obj
->swapout
.sio
, how
);
284 storeSwapOutFileClosed(void *data
, int errflag
, StoreIOState::Pointer self
)
287 static_cast<generic_cbdata
*>(data
)->unwrap(&e
);
289 MemObject
*mem
= e
->mem_obj
;
290 assert(mem
->swapout
.sio
== self
);
291 assert(e
->swap_status
== SWAPOUT_WRITING
);
293 // if object_size is still unknown, the entry was probably aborted
294 if (errflag
|| e
->objectLen() < 0) {
295 debugs(20, 2, "storeSwapOutFileClosed: dirno " << e
->swap_dirn
<< ", swapfile " <<
296 std::hex
<< std::setw(8) << std::setfill('0') << std::uppercase
<<
297 e
->swap_filen
<< ", errflag=" << errflag
);
299 if (errflag
== DISK_NO_SPACE_LEFT
) {
300 /* FIXME: this should be handle by the link from store IO to
301 * Store, rather than being a top level API call.
303 e
->disk().diskFull();
307 if (e
->swap_filen
>= 0)
308 e
->disk().unlink(*e
);
310 assert(e
->swap_status
== SWAPOUT_NONE
);
314 /* swapping complete */
315 debugs(20, 3, "storeSwapOutFileClosed: SwapOut complete: '" << e
->url() << "' to " <<
316 e
->swap_dirn
<< ", " << std::hex
<< std::setw(8) << std::setfill('0') <<
317 std::uppercase
<< e
->swap_filen
);
318 debugs(20, 5, HERE
<< "swap_file_sz = " <<
319 e
->objectLen() << " + " << mem
->swap_hdr_sz
);
321 e
->swap_file_sz
= e
->objectLen() + mem
->swap_hdr_sz
;
322 e
->swap_status
= SWAPOUT_DONE
;
323 e
->disk().swappedOut(*e
);
325 // XXX: For some Stores, it is pointless to re-check cachability here
326 // and it leads to double counts in store_check_cachable_hist. We need
327 // another way to signal a completed but failed swapout. Or, better,
328 // each Store should handle its own logging and LOG state setting.
329 if (e
->checkCachable()) {
330 storeLog(STORE_LOG_SWAPOUT
, e
);
331 storeDirSwapLog(e
, SWAP_LOG_ADD
);
334 ++statCounter
.swap
.outs
;
337 debugs(20, 3, "storeSwapOutFileClosed: " << __FILE__
<< ":" << __LINE__
);
338 mem
->swapout
.sio
= NULL
;
339 e
->unlock("storeSwapOutFileClosed");
343 StoreEntry::mayStartSwapOut()
345 // must be checked in the caller
346 assert(!EBIT_TEST(flags
, ENTRY_ABORTED
));
347 assert(!swappingOut());
349 if (!Config
.cacheSwap
.n_configured
)
353 const MemObject::SwapOut::Decision
&decision
= mem_obj
->swapout
.decision
;
355 // if we decided that starting is not possible, do not repeat same checks
356 if (decision
== MemObject::SwapOut::swImpossible
) {
357 debugs(20, 3, HERE
<< " already rejected");
361 // if we swapped out already, do not start over
362 if (swap_status
== SWAPOUT_DONE
) {
363 debugs(20, 3, "already did");
364 swapOutDecision(MemObject::SwapOut::swImpossible
);
368 // if we stared swapping out already, do not start over
369 if (decision
== MemObject::SwapOut::swStarted
) {
370 debugs(20, 3, "already started");
371 swapOutDecision(MemObject::SwapOut::swImpossible
);
375 // if we decided that swapout is possible, do not repeat same checks
376 if (decision
== MemObject::SwapOut::swPossible
) {
377 debugs(20, 3, "already allowed");
381 if (!checkCachable()) {
382 debugs(20, 3, HERE
<< "not cachable");
383 swapOutDecision(MemObject::SwapOut::swImpossible
);
387 if (EBIT_TEST(flags
, ENTRY_SPECIAL
)) {
388 debugs(20, 3, HERE
<< url() << " SPECIAL");
389 swapOutDecision(MemObject::SwapOut::swImpossible
);
393 if (mem_obj
->inmem_lo
> 0) {
394 debugs(20, 3, "storeSwapOut: (inmem_lo > 0) imem_lo:" << mem_obj
->inmem_lo
);
395 swapOutDecision(MemObject::SwapOut::swImpossible
);
399 if (!mem_obj
->isContiguous()) {
400 debugs(20, 3, "storeSwapOut: not Contiguous");
401 swapOutDecision(MemObject::SwapOut::swImpossible
);
405 // handle store_maxobjsize limit
407 // TODO: add estimated store metadata size to be conservative
409 // use guaranteed maximum if it is known
410 const int64_t expectedEnd
= mem_obj
->expectedReplySize();
411 debugs(20, 7, HERE
<< "expectedEnd = " << expectedEnd
);
412 if (expectedEnd
> store_maxobjsize
) {
413 debugs(20, 3, HERE
<< "will not fit: " << expectedEnd
<<
414 " > " << store_maxobjsize
);
415 swapOutDecision(MemObject::SwapOut::swImpossible
);
416 return false; // known to outgrow the limit eventually
419 // use current minimum (always known)
420 const int64_t currentEnd
= mem_obj
->endOffset();
421 if (currentEnd
> store_maxobjsize
) {
422 debugs(20, 3, HERE
<< "does not fit: " << currentEnd
<<
423 " > " << store_maxobjsize
);
424 swapOutDecision(MemObject::SwapOut::swImpossible
);
425 return false; // already does not fit and may only get bigger
428 // prevent final default swPossible answer for yet unknown length
429 if (expectedEnd
< 0 && store_status
!= STORE_OK
) {
430 const int64_t more
= Store::Root().accumulateMore(*this);
432 debugs(20, 5, "got " << currentEnd
<< "; defer decision for " << more
<< " more bytes");
433 return true; // may still fit, but no final decision yet
438 swapOutDecision(MemObject::SwapOut::swPossible
);