]>
Commit | Line | Data |
---|---|---|
9cef6668 | 1 | |
2 | /* | |
262a0e14 | 3 | * $Id$ |
9cef6668 | 4 | * |
5 | * DEBUG: section 20 Storage Manager Swapout Functions | |
6 | * AUTHOR: Duane Wessels | |
7 | * | |
2b6662ba | 8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
9cef6668 | 9 | * ---------------------------------------------------------- |
10 | * | |
2b6662ba | 11 | * Squid is the result of efforts by numerous individuals from |
12 | * the Internet community; see the CONTRIBUTORS file for full | |
13 | * details. Many organizations have provided support for Squid's | |
14 | * development; see the SPONSORS file for full details. Squid is | |
15 | * Copyrighted (C) 2001 by the Regents of the University of | |
16 | * California; see the COPYRIGHT file for full details. Squid | |
17 | * incorporates software developed and/or copyrighted by other | |
18 | * sources; see the CREDITS file for full details. | |
9cef6668 | 19 | * |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
26ac0430 | 24 | * |
9cef6668 | 25 | * This program is distributed in the hope that it will be useful, |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
26ac0430 | 29 | * |
9cef6668 | 30 | * You should have received a copy of the GNU General Public License |
31 | * along with this program; if not, write to the Free Software | |
32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
33 | * | |
34 | */ | |
35 | ||
f09f5b26 | 36 | #include "squid.h" |
aa839030 | 37 | #include "cbdata.h" |
c8be6d7b | 38 | #include "StoreClient.h" |
e6ccf245 | 39 | #include "Store.h" |
528b2c61 | 40 | /* FIXME: Abstract the use of this more */ |
41 | #include "mem_node.h" | |
42 | #include "MemObject.h" | |
d3b3ab85 | 43 | #include "SwapDir.h" |
4b981814 | 44 | #include "swap_log_op.h" |
f09f5b26 | 45 | |
d54f0ab3 | 46 | static void storeSwapOutStart(StoreEntry * e); |
4fcc8876 | 47 | static StoreIOState::STIOCB storeSwapOutFileClosed; |
48 | static StoreIOState::STFNCB storeSwapOutFileNotify; | |
f09f5b26 | 49 | |
daacd51f AJ |
50 | // wrapper to cross C/C++ ABI boundary. xfree is extern "C" for libraries. |
51 | static void xfree_cppwrapper(void *x) | |
52 | { | |
53 | xfree(x); | |
54 | } | |
55 | ||
f09f5b26 | 56 | /* start swapping object to disk */ |
d54f0ab3 | 57 | static void |
f09f5b26 | 58 | storeSwapOutStart(StoreEntry * e) |
59 | { | |
2391a162 | 60 | MemObject *mem = e->mem_obj; |
d3b3ab85 | 61 | StoreIOState::Pointer sio; |
2391a162 | 62 | assert(mem); |
cd748f27 | 63 | /* Build the swap metadata, so the filesystem will know how much |
64 | * metadata there is to store | |
65 | */ | |
26ac0430 AJ |
66 | debugs(20, 5, "storeSwapOutStart: Begin SwapOut '" << e->url() << "' to dirno " << |
67 | e->swap_dirn << ", fileno " << std::hex << std::setw(8) << std::setfill('0') << | |
bf8fe701 | 68 | std::uppercase << e->swap_filen); |
cd748f27 | 69 | e->swap_status = SWAPOUT_WRITING; |
528b2c61 | 70 | /* If we start swapping out objects with OutOfBand Metadata, |
71 | * then this code needs changing | |
72 | */ | |
246e6cc1 AJ |
73 | |
74 | /* TODO: make some sort of data,size refcounted immutable buffer | |
75 | * and stop fooling ourselves with "const char*" buffers. | |
76 | */ | |
77 | ||
78 | // Create metadata now, possibly in vain: storeCreate needs swap_hdr_sz. | |
79 | const char *buf = e->getSerialisedMetaData (); | |
80 | assert(buf); | |
81 | ||
cd748f27 | 82 | /* Create the swap file */ |
aa839030 | 83 | generic_cbdata *c = new generic_cbdata(e); |
fa80a8ef | 84 | sio = storeCreate(e, storeSwapOutFileNotify, storeSwapOutFileClosed, c); |
62e76326 | 85 | |
85a4b153 | 86 | if (sio == NULL) { |
62e76326 | 87 | e->swap_status = SWAPOUT_NONE; |
aa1a691e AR |
88 | // our caller thinks SWAPOUT_NONE means swapping out has not started |
89 | // yet so we better release here to avoid being called again and again | |
90 | e->releaseRequest(); | |
aa839030 | 91 | delete c; |
246e6cc1 | 92 | xfree((char*)buf); |
62e76326 | 93 | storeLog(STORE_LOG_SWAPOUTFAIL, e); |
94 | return; | |
3fc29499 | 95 | } |
62e76326 | 96 | |
528b2c61 | 97 | mem->swapout.sio = sio; |
98 | /* Don't lock until after create, or the replacement | |
99 | * code might get confused */ | |
34266cde | 100 | |
3d0ac046 | 101 | e->lock(); |
cd748f27 | 102 | /* Pick up the file number if it was assigned immediately */ |
103 | e->swap_filen = mem->swapout.sio->swap_filen; | |
34266cde | 104 | |
cd748f27 | 105 | e->swap_dirn = mem->swapout.sio->swap_dirn; |
34266cde | 106 | |
cd748f27 | 107 | /* write out the swap metadata */ |
daacd51f | 108 | storeIOWrite(mem->swapout.sio, buf, mem->swap_hdr_sz, 0, xfree_cppwrapper); |
f09f5b26 | 109 | } |
110 | ||
cd748f27 | 111 | static void |
e5de8b13 | 112 | storeSwapOutFileNotify(void *data, int errflag, StoreIOState::Pointer self) |
cd748f27 | 113 | { |
e6ccf245 | 114 | generic_cbdata *c = (generic_cbdata *)data; |
115 | StoreEntry *e = (StoreEntry *)c->data; | |
cd748f27 | 116 | MemObject *mem = e->mem_obj; |
117 | assert(e->swap_status == SWAPOUT_WRITING); | |
118 | assert(mem); | |
84177444 | 119 | assert(mem->swapout.sio == self); |
cd748f27 | 120 | assert(errflag == 0); |
f58bb2f4 | 121 | assert(e->swap_filen < 0); // if this fails, call SwapDir::disconnect(e) |
cd748f27 | 122 | e->swap_filen = mem->swapout.sio->swap_filen; |
123 | e->swap_dirn = mem->swapout.sio->swap_dirn; | |
124 | } | |
125 | ||
528b2c61 | 126 | static void |
127 | doPages(StoreEntry *anEntry) | |
128 | { | |
129 | MemObject *mem = anEntry->mem_obj; | |
62e76326 | 130 | |
528b2c61 | 131 | do { |
aa1a691e AR |
132 | // find the page containing the first byte we have not swapped out yet |
133 | mem_node *page = | |
134 | mem->data_hdr.getBlockContainingLocation(mem->swapout.queue_offset); | |
62e76326 | 135 | |
aa1a691e AR |
136 | if (!page) |
137 | return; // wait for more data to become available | |
138 | ||
139 | // memNodeWriteComplete() and absence of buffer offset math below | |
140 | // imply that we always write from the very beginning of the page | |
141 | assert(page->start() == mem->swapout.queue_offset); | |
62e76326 | 142 | |
143 | /* | |
144 | * Get the length of this buffer. We are assuming(!) that the buffer | |
145 | * length won't change on this buffer, or things are going to be very | |
146 | * strange. I think that after the copy to a buffer is done, the buffer | |
147 | * size should stay fixed regardless so that this code isn't confused, | |
148 | * but we can look at this at a later date or whenever the code results | |
149 | * in bad swapouts, whichever happens first. :-) | |
150 | */ | |
aa1a691e | 151 | ssize_t swap_buf_len = page->nodeBuffer.length; |
62e76326 | 152 | |
4a7a3d56 | 153 | debugs(20, 3, "storeSwapOut: swap_buf_len = " << swap_buf_len); |
62e76326 | 154 | |
155 | assert(swap_buf_len > 0); | |
156 | ||
4a7a3d56 | 157 | debugs(20, 3, "storeSwapOut: swapping out " << swap_buf_len << " bytes from " << mem->swapout.queue_offset); |
62e76326 | 158 | |
159 | mem->swapout.queue_offset += swap_buf_len; | |
160 | ||
84177444 | 161 | storeIOWrite(mem->swapout.sio, |
aa1a691e | 162 | mem->data_hdr.NodeGet(page), |
84177444 | 163 | swap_buf_len, |
164 | -1, | |
165 | memNodeWriteComplete); | |
62e76326 | 166 | |
167 | /* the storeWrite() call might generate an error */ | |
168 | if (anEntry->swap_status != SWAPOUT_WRITING) | |
169 | break; | |
170 | ||
47f6e231 | 171 | int64_t swapout_size = mem->endOffset() - mem->swapout.queue_offset; |
62e76326 | 172 | |
173 | if (anEntry->store_status == STORE_PENDING) | |
174 | if (swapout_size < SM_PAGE_SIZE) | |
175 | break; | |
176 | ||
177 | if (swapout_size <= 0) | |
178 | return; | |
528b2c61 | 179 | } while (true); |
180 | } | |
181 | ||
182 | ||
183 | /* This routine is called every time data is sent to the client side. | |
184 | * It's overhead is therefor, significant. | |
185 | */ | |
f09f5b26 | 186 | void |
c07cbbf4 | 187 | StoreEntry::swapOut() |
f09f5b26 | 188 | { |
c07cbbf4 | 189 | if (!mem_obj) |
62e76326 | 190 | return; |
191 | ||
c07cbbf4 | 192 | if (!swapoutPossible()) |
62e76326 | 193 | return; |
528b2c61 | 194 | |
aa1a691e AR |
195 | // Aborted entries have STORE_OK, but swapoutPossible rejects them. Thus, |
196 | // store_status == STORE_OK below means we got everything we wanted. | |
197 | ||
47f6e231 | 198 | debugs(20, 7, HERE << "storeSwapOut: mem->inmem_lo = " << mem_obj->inmem_lo); |
199 | debugs(20, 7, HERE << "storeSwapOut: mem->endOffset() = " << mem_obj->endOffset()); | |
200 | debugs(20, 7, HERE << "storeSwapOut: swapout.queue_offset = " << mem_obj->swapout.queue_offset); | |
62e76326 | 201 | |
85a4b153 | 202 | if (mem_obj->swapout.sio != NULL) |
26ac0430 | 203 | debugs(20, 7, "storeSwapOut: storeOffset() = " << mem_obj->swapout.sio->offset() ); |
62e76326 | 204 | |
aa1a691e | 205 | // buffered bytes we have not swapped out yet |
47f6e231 | 206 | int64_t swapout_maxsize = mem_obj->endOffset() - mem_obj->swapout.queue_offset; |
62e76326 | 207 | |
528b2c61 | 208 | assert(swapout_maxsize >= 0); |
62e76326 | 209 | |
47f6e231 | 210 | int64_t const lowest_offset = mem_obj->lowestMemReaderOffset(); |
62e76326 | 211 | |
47f6e231 | 212 | debugs(20, 7, HERE << "storeSwapOut: lowest_offset = " << lowest_offset); |
62e76326 | 213 | |
aa1a691e AR |
214 | // Check to see whether we're going to defer the swapout based upon size |
215 | if (store_status != STORE_OK) { | |
216 | const int64_t expectedSize = mem_obj->expectedReplySize(); | |
217 | const int64_t maxKnownSize = expectedSize < 0 ? | |
218 | swapout_maxsize : expectedSize; | |
219 | debugs(20, 7, HERE << "storeSwapOut: maxKnownSize= " << maxKnownSize); | |
220 | ||
221 | if (maxKnownSize < store_maxobjsize) { | |
222 | /* | |
223 | * NOTE: the store_maxobjsize here is the max of optional | |
224 | * max-size values from 'cache_dir' lines. It is not the | |
225 | * same as 'maximum_object_size'. By default, store_maxobjsize | |
226 | * will be set to -1. However, I am worried that this | |
227 | * deferance may consume a lot of memory in some cases. | |
228 | * Should we add an option to limit this memory consumption? | |
229 | */ | |
230 | debugs(20, 5, "storeSwapOut: Deferring swapout start for " << | |
231 | (store_maxobjsize - maxKnownSize) << " bytes"); | |
232 | return; | |
233 | } | |
cd748f27 | 234 | } |
62e76326 | 235 | |
aa1a691e | 236 | // TODO: it is better to trim as soon as we swap something out, not before |
c07cbbf4 | 237 | trimMemory(); |
5aecb102 | 238 | #if SIZEOF_OFF_T <= 4 |
62e76326 | 239 | |
c07cbbf4 | 240 | if (mem_obj->endOffset() > 0x7FFF0000) { |
c664b2a6 | 241 | debugs(20, 0, "WARNING: preventing off_t overflow for " << url()); |
bfb55b6f | 242 | abort(); |
62e76326 | 243 | return; |
adcceb47 | 244 | } |
62e76326 | 245 | |
adcceb47 | 246 | #endif |
c07cbbf4 | 247 | if (swap_status == SWAPOUT_WRITING) |
47f6e231 | 248 | assert(mem_obj->inmem_lo <= mem_obj->objectBytesOnDisk() ); |
62e76326 | 249 | |
c07cbbf4 | 250 | if (!swapOutAble()) |
62e76326 | 251 | return; |
252 | ||
4a7a3d56 | 253 | debugs(20, 7, "storeSwapOut: swapout_size = " << swapout_maxsize); |
62e76326 | 254 | |
aa1a691e AR |
255 | if (swapout_maxsize == 0) { // swapped everything we got |
256 | if (store_status == STORE_OK) { // got everything we wanted | |
257 | assert(mem_obj->object_sz >= 0); | |
258 | swapOutFileClose(StoreIOState::wroteAll); | |
259 | } | |
260 | // else need more data to swap out | |
261 | return; | |
1b72bda1 | 262 | } |
62e76326 | 263 | |
c07cbbf4 | 264 | if (store_status == STORE_PENDING) { |
62e76326 | 265 | /* wait for a full block to write */ |
266 | ||
267 | if (swapout_maxsize < SM_PAGE_SIZE) | |
268 | return; | |
269 | ||
270 | /* | |
271 | * Wait until we are below the disk FD limit, only if the | |
272 | * next server-side read won't be deferred. | |
273 | */ | |
c07cbbf4 | 274 | if (storeTooManyDiskFilesOpen() && !checkDeferRead(-1)) |
62e76326 | 275 | return; |
c47511fd | 276 | } |
62e76326 | 277 | |
2391a162 | 278 | /* Ok, we have stuff to swap out. Is there a swapout.sio open? */ |
c07cbbf4 | 279 | if (swap_status == SWAPOUT_NONE) { |
280 | assert(mem_obj->swapout.sio == NULL); | |
281 | assert(mem_obj->inmem_lo == 0); | |
62e76326 | 282 | |
3900307b | 283 | if (checkCachable()) |
c07cbbf4 | 284 | storeSwapOutStart(this); |
62e76326 | 285 | else |
286 | return; | |
287 | ||
288 | /* ENTRY_CACHABLE will be cleared and we'll never get here again */ | |
f09f5b26 | 289 | } |
62e76326 | 290 | |
c07cbbf4 | 291 | if (mem_obj->swapout.sio == NULL) |
62e76326 | 292 | return; |
293 | ||
c07cbbf4 | 294 | doPages(this); |
62e76326 | 295 | |
85a4b153 | 296 | if (mem_obj->swapout.sio == NULL) |
62e76326 | 297 | /* oops, we're not swapping out any more */ |
298 | return; | |
299 | ||
c07cbbf4 | 300 | if (store_status == STORE_OK) { |
62e76326 | 301 | /* |
302 | * If the state is STORE_OK, then all data must have been given | |
303 | * to the filesystem at this point because storeSwapOut() is | |
304 | * not going to be called again for this entry. | |
305 | */ | |
aa1a691e | 306 | assert(mem_obj->object_sz >= 0); |
c07cbbf4 | 307 | assert(mem_obj->endOffset() == mem_obj->swapout.queue_offset); |
aa1a691e | 308 | swapOutFileClose(StoreIOState::wroteAll); |
1792fbdb | 309 | } |
f09f5b26 | 310 | } |
311 | ||
312 | void | |
aa1a691e | 313 | StoreEntry::swapOutFileClose(int how) |
f09f5b26 | 314 | { |
c07cbbf4 | 315 | assert(mem_obj != NULL); |
aa1a691e | 316 | debugs(20, 3, "storeSwapOutFileClose: " << getMD5Text() << " how=" << how); |
bf8fe701 | 317 | debugs(20, 3, "storeSwapOutFileClose: sio = " << mem_obj->swapout.sio.getRaw()); |
62e76326 | 318 | |
c07cbbf4 | 319 | if (mem_obj->swapout.sio == NULL) |
62e76326 | 320 | return; |
321 | ||
aa1a691e | 322 | storeClose(mem_obj->swapout.sio, how); |
f09f5b26 | 323 | } |
324 | ||
325 | static void | |
e5de8b13 | 326 | storeSwapOutFileClosed(void *data, int errflag, StoreIOState::Pointer self) |
f09f5b26 | 327 | { |
e6ccf245 | 328 | generic_cbdata *c = (generic_cbdata *)data; |
329 | StoreEntry *e = (StoreEntry *)c->data; | |
25354045 | 330 | MemObject *mem = e->mem_obj; |
84177444 | 331 | assert(mem->swapout.sio == self); |
2391a162 | 332 | assert(e->swap_status == SWAPOUT_WRITING); |
333 | cbdataFree(c); | |
62e76326 | 334 | |
aa1a691e AR |
335 | // if object_size is still unknown, the entry was probably aborted |
336 | if (errflag || e->objectLen() < 0) { | |
de4bc0ea | 337 | debugs(20, 2, "storeSwapOutFileClosed: dirno " << e->swap_dirn << ", swapfile " << |
26ac0430 | 338 | std::hex << std::setw(8) << std::setfill('0') << std::uppercase << |
bf8fe701 | 339 | e->swap_filen << ", errflag=" << errflag); |
62e76326 | 340 | |
341 | if (errflag == DISK_NO_SPACE_LEFT) { | |
c8f4eac4 | 342 | /* FIXME: this should be handle by the link from store IO to |
343 | * Store, rather than being a top level API call. | |
344 | */ | |
345 | e->store()->diskFull(); | |
62e76326 | 346 | storeConfigure(); |
347 | } | |
348 | ||
fa192c71 | 349 | if (e->swap_filen >= 0) |
c8f4eac4 | 350 | e->unlink(); |
62e76326 | 351 | |
f58bb2f4 | 352 | assert(e->swap_status == SWAPOUT_NONE); |
62e76326 | 353 | |
d88e3c49 | 354 | e->releaseRequest(); |
2391a162 | 355 | } else { |
62e76326 | 356 | /* swapping complete */ |
26ac0430 AJ |
357 | debugs(20, 3, "storeSwapOutFileClosed: SwapOut complete: '" << e->url() << "' to " << |
358 | e->swap_dirn << ", " << std::hex << std::setw(8) << std::setfill('0') << | |
bf8fe701 | 359 | std::uppercase << e->swap_filen); |
aa1a691e AR |
360 | debugs(20, 5, HERE << "swap_file_sz = " << |
361 | e->objectLen() << " + " << mem->swap_hdr_sz); | |
362 | assert(e->objectLen() >= 0); // we checked that above | |
707fdc47 | 363 | e->swap_file_sz = e->objectLen() + mem->swap_hdr_sz; |
62e76326 | 364 | e->swap_status = SWAPOUT_DONE; |
c8f4eac4 | 365 | e->store()->updateSize(e->swap_file_sz, 1); |
62e76326 | 366 | |
3900307b | 367 | if (e->checkCachable()) { |
62e76326 | 368 | storeLog(STORE_LOG_SWAPOUT, e); |
369 | storeDirSwapLog(e, SWAP_LOG_ADD); | |
370 | } | |
371 | ||
372 | statCounter.swap.outs++; | |
f09f5b26 | 373 | } |
62e76326 | 374 | |
bf8fe701 | 375 | debugs(20, 3, "storeSwapOutFileClosed: " << __FILE__ << ":" << __LINE__); |
d3b3ab85 | 376 | mem->swapout.sio = NULL; |
97b5e68f | 377 | e->unlock(); |
f09f5b26 | 378 | } |
61038223 | 379 | |
c2725718 | 380 | /* |
381 | * Is this entry a candidate for writing to disk? | |
382 | */ | |
c07cbbf4 | 383 | bool |
384 | StoreEntry::swapOutAble() const | |
c2725718 | 385 | { |
06d2839d | 386 | dlink_node *node; |
62e76326 | 387 | |
85a4b153 | 388 | if (mem_obj->swapout.sio != NULL) |
c07cbbf4 | 389 | return true; |
62e76326 | 390 | |
c07cbbf4 | 391 | if (mem_obj->inmem_lo > 0) |
392 | return false; | |
62e76326 | 393 | |
5f25e839 | 394 | /* |
395 | * If there are DISK clients, we must write to disk | |
396 | * even if its not cachable | |
528b2c61 | 397 | * RBC: Surely we should not create disk client on non cacheable objects? |
398 | * therefore this should be an assert? | |
4e70dae3 | 399 | * RBC 20030708: We can use disk to avoid mem races, so this shouldn't be |
400 | * an assert. | |
5f25e839 | 401 | */ |
c07cbbf4 | 402 | for (node = mem_obj->clients.head; node; node = node->next) { |
62e76326 | 403 | if (((store_client *) node->data)->getType() == STORE_DISK_CLIENT) |
c07cbbf4 | 404 | return true; |
06d2839d | 405 | } |
62e76326 | 406 | |
a56b2212 | 407 | /* Don't pollute the disk with icons and other special entries */ |
c07cbbf4 | 408 | if (EBIT_TEST(flags, ENTRY_SPECIAL)) |
409 | return false; | |
62e76326 | 410 | |
c07cbbf4 | 411 | if (!EBIT_TEST(flags, ENTRY_CACHABLE)) |
412 | return false; | |
62e76326 | 413 | |
c07cbbf4 | 414 | if (!mem_obj->isContiguous()) |
415 | return false; | |
62e76326 | 416 | |
c07cbbf4 | 417 | return true; |
c2725718 | 418 | } |