]>
Commit | Line | Data |
---|---|---|
9cef6668 | 1 | |
2 | /* | |
4fcc8876 | 3 | * $Id: store_swapout.cc,v 1.105 2006/05/23 00:21:47 wessels Exp $ |
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. | |
24 | * | |
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. | |
29 | * | |
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" |
c8be6d7b | 37 | #include "StoreClient.h" |
e6ccf245 | 38 | #include "Store.h" |
528b2c61 | 39 | /* FIXME: Abstract the use of this more */ |
40 | #include "mem_node.h" | |
41 | #include "MemObject.h" | |
d3b3ab85 | 42 | #include "SwapDir.h" |
f09f5b26 | 43 | |
d54f0ab3 | 44 | static void storeSwapOutStart(StoreEntry * e); |
4fcc8876 | 45 | static StoreIOState::STIOCB storeSwapOutFileClosed; |
46 | static StoreIOState::STFNCB storeSwapOutFileNotify; | |
f09f5b26 | 47 | |
48 | /* start swapping object to disk */ | |
d54f0ab3 | 49 | static void |
f09f5b26 | 50 | storeSwapOutStart(StoreEntry * e) |
51 | { | |
2391a162 | 52 | MemObject *mem = e->mem_obj; |
d3b3ab85 | 53 | StoreIOState::Pointer sio; |
2391a162 | 54 | assert(mem); |
cd748f27 | 55 | /* Build the swap metadata, so the filesystem will know how much |
56 | * metadata there is to store | |
57 | */ | |
58 | debug(20, 5) ("storeSwapOutStart: Begin SwapOut '%s' to dirno %d, fileno %08X\n", | |
62e76326 | 59 | storeUrl(e), e->swap_dirn, e->swap_filen); |
cd748f27 | 60 | e->swap_status = SWAPOUT_WRITING; |
528b2c61 | 61 | /* If we start swapping out objects with OutOfBand Metadata, |
62 | * then this code needs changing | |
63 | */ | |
cd748f27 | 64 | /* Create the swap file */ |
528b2c61 | 65 | generic_cbdata *c = cbdataAlloc(generic_cbdata); |
2391a162 | 66 | c->data = e; |
fa80a8ef | 67 | sio = storeCreate(e, storeSwapOutFileNotify, storeSwapOutFileClosed, c); |
62e76326 | 68 | |
528b2c61 | 69 | if (NULL == sio.getRaw()) { |
62e76326 | 70 | e->swap_status = SWAPOUT_NONE; |
ad8f212f | 71 | cbdataFree(c); |
62e76326 | 72 | storeLog(STORE_LOG_SWAPOUTFAIL, e); |
73 | return; | |
3fc29499 | 74 | } |
62e76326 | 75 | |
528b2c61 | 76 | mem->swapout.sio = sio; |
77 | /* Don't lock until after create, or the replacement | |
78 | * code might get confused */ | |
34266cde | 79 | |
80 | e->lock() | |
81 | ||
82 | ; | |
cd748f27 | 83 | /* Pick up the file number if it was assigned immediately */ |
84 | e->swap_filen = mem->swapout.sio->swap_filen; | |
34266cde | 85 | |
cd748f27 | 86 | e->swap_dirn = mem->swapout.sio->swap_dirn; |
34266cde | 87 | |
cd748f27 | 88 | /* write out the swap metadata */ |
528b2c61 | 89 | /* TODO: make some sort of data,size refcounted immutable buffer |
90 | * for use by this sort of function. | |
91 | */ | |
92 | char const *buf = e->getSerialisedMetaData (); | |
34266cde | 93 | |
528b2c61 | 94 | /* If we start swapping out with out of band metadata, this assert |
95 | * will catch it - this code needs to be adjusted if that happens | |
96 | */ | |
97 | assert (buf); | |
34266cde | 98 | |
528b2c61 | 99 | storeIOWrite(mem->swapout.sio, buf, mem->swap_hdr_sz, 0, xfree); |
f09f5b26 | 100 | } |
101 | ||
cd748f27 | 102 | static void |
e877aaac | 103 | storeSwapOutFileNotify(void *data, int errflag) |
cd748f27 | 104 | { |
e6ccf245 | 105 | generic_cbdata *c = (generic_cbdata *)data; |
106 | StoreEntry *e = (StoreEntry *)c->data; | |
cd748f27 | 107 | MemObject *mem = e->mem_obj; |
108 | assert(e->swap_status == SWAPOUT_WRITING); | |
109 | assert(mem); | |
e877aaac | 110 | assert(mem->swapout.sio != NULL); |
cd748f27 | 111 | assert(errflag == 0); |
112 | e->swap_filen = mem->swapout.sio->swap_filen; | |
113 | e->swap_dirn = mem->swapout.sio->swap_dirn; | |
114 | } | |
115 | ||
528b2c61 | 116 | static void |
117 | doPages(StoreEntry *anEntry) | |
118 | { | |
119 | MemObject *mem = anEntry->mem_obj; | |
62e76326 | 120 | |
528b2c61 | 121 | do { |
62e76326 | 122 | /* |
123 | * Evil hack time. | |
124 | * We are paging out to disk in page size chunks. however, later on when | |
125 | * we update the queue position, we might not have a page (I *think*), | |
126 | * so we do the actual page update here. | |
127 | */ | |
128 | ||
129 | if (mem->swapout.memnode == NULL) { | |
130 | /* We need to swap out the first page */ | |
42a503bd | 131 | mem->swapout.memnode = const_cast<mem_node *>(mem->data_hdr.start()); |
62e76326 | 132 | } else { |
133 | /* We need to swap out the next page */ | |
42a503bd | 134 | /* 20030636 RBC - we don't have ->next anymore. |
135 | * But we do have the next location */ | |
136 | mem->swapout.memnode = mem->data_hdr.getBlockContainingLocation (mem->swapout.memnode->end()); | |
62e76326 | 137 | } |
138 | ||
139 | /* | |
140 | * Get the length of this buffer. We are assuming(!) that the buffer | |
141 | * length won't change on this buffer, or things are going to be very | |
142 | * strange. I think that after the copy to a buffer is done, the buffer | |
143 | * size should stay fixed regardless so that this code isn't confused, | |
144 | * but we can look at this at a later date or whenever the code results | |
145 | * in bad swapouts, whichever happens first. :-) | |
146 | */ | |
147 | ssize_t swap_buf_len = mem->swapout.memnode->nodeBuffer.length; | |
148 | ||
149 | debug(20, 3) ("storeSwapOut: swap_buf_len = %d\n", (int) swap_buf_len); | |
150 | ||
151 | assert(swap_buf_len > 0); | |
152 | ||
153 | debug(20, 3) ("storeSwapOut: swapping out %ld bytes from %ld\n", | |
154 | (long int) swap_buf_len, (long int) mem->swapout.queue_offset); | |
155 | ||
156 | mem->swapout.queue_offset += swap_buf_len; | |
157 | ||
12d74f96 | 158 | storeIOWrite(mem->swapout.sio, mem->data_hdr.NodeGet(mem->swapout.memnode), swap_buf_len, -1, memNodeWriteComplete); |
62e76326 | 159 | |
160 | /* the storeWrite() call might generate an error */ | |
161 | if (anEntry->swap_status != SWAPOUT_WRITING) | |
162 | break; | |
163 | ||
164 | ssize_t swapout_size = (ssize_t) (mem->endOffset() - mem->swapout.queue_offset); | |
165 | ||
166 | if (anEntry->store_status == STORE_PENDING) | |
167 | if (swapout_size < SM_PAGE_SIZE) | |
168 | break; | |
169 | ||
170 | if (swapout_size <= 0) | |
171 | return; | |
528b2c61 | 172 | } while (true); |
173 | } | |
174 | ||
175 | ||
176 | /* This routine is called every time data is sent to the client side. | |
177 | * It's overhead is therefor, significant. | |
178 | */ | |
f09f5b26 | 179 | void |
2391a162 | 180 | storeSwapOut(StoreEntry * e) |
f09f5b26 | 181 | { |
528b2c61 | 182 | if (!e->mem_obj) |
62e76326 | 183 | return; |
184 | ||
528b2c61 | 185 | if (!e->swapoutPossible()) |
62e76326 | 186 | return; |
528b2c61 | 187 | |
188 | MemObject *mem = e->mem_obj; | |
189 | ||
2391a162 | 190 | debug(20, 7) ("storeSwapOut: mem->inmem_lo = %d\n", |
62e76326 | 191 | (int) mem->inmem_lo); |
192 | ||
528b2c61 | 193 | debug(20, 7) ("storeSwapOut: mem->endOffset() = %d\n", |
62e76326 | 194 | (int) mem->endOffset()); |
195 | ||
2391a162 | 196 | debug(20, 7) ("storeSwapOut: swapout.queue_offset = %d\n", |
62e76326 | 197 | (int) mem->swapout.queue_offset); |
198 | ||
d3b3ab85 | 199 | if (mem->swapout.sio.getRaw()) |
62e76326 | 200 | debug(20, 7) ("storeSwapOut: storeOffset() = %d\n", |
201 | (int) mem->swapout.sio->offset()); | |
202 | ||
528b2c61 | 203 | ssize_t swapout_maxsize = (ssize_t) (mem->endOffset() - mem->swapout.queue_offset); |
62e76326 | 204 | |
528b2c61 | 205 | assert(swapout_maxsize >= 0); |
62e76326 | 206 | |
528b2c61 | 207 | off_t const lowest_offset = mem->lowestMemReaderOffset(); |
62e76326 | 208 | |
2391a162 | 209 | debug(20, 7) ("storeSwapOut: lowest_offset = %d\n", |
62e76326 | 210 | (int) lowest_offset); |
211 | ||
cd748f27 | 212 | /* |
213 | * Grab the swapout_size and check to see whether we're going to defer | |
214 | * the swapout based upon size | |
215 | */ | |
528b2c61 | 216 | if ((e->store_status != STORE_OK) && (swapout_maxsize < store_maxobjsize)) { |
62e76326 | 217 | /* |
218 | * NOTE: the store_maxobjsize here is the max of optional | |
219 | * max-size values from 'cache_dir' lines. It is not the | |
220 | * same as 'maximum_object_size'. By default, store_maxobjsize | |
221 | * will be set to -1. However, I am worried that this | |
222 | * deferance may consume a lot of memory in some cases. | |
223 | * It would be good to make this decision based on reply | |
224 | * content-length, rather than wait to accumulate huge | |
225 | * amounts of object data in memory. | |
226 | */ | |
227 | debug(20, 5) ("storeSwapOut: Deferring starting swapping out\n"); | |
228 | return; | |
cd748f27 | 229 | } |
62e76326 | 230 | |
528b2c61 | 231 | e->trimMemory(); |
adcceb47 | 232 | #if SIZEOF_OFF_T == 4 |
62e76326 | 233 | |
528b2c61 | 234 | if (mem->endOffset() > 0x7FFF0000) { |
62e76326 | 235 | debug(20, 0) ("WARNING: preventing off_t overflow for %s\n", storeUrl(e)); |
236 | storeAbort(e); | |
237 | return; | |
adcceb47 | 238 | } |
62e76326 | 239 | |
adcceb47 | 240 | #endif |
c2725718 | 241 | if (e->swap_status == SWAPOUT_WRITING) |
62e76326 | 242 | assert(mem->inmem_lo <= (off_t)mem->objectBytesOnDisk() ); |
243 | ||
c2725718 | 244 | if (!storeSwapOutAble(e)) |
62e76326 | 245 | return; |
246 | ||
2391a162 | 247 | debug(20, 7) ("storeSwapOut: swapout_size = %d\n", |
62e76326 | 248 | (int) swapout_maxsize); |
249 | ||
528b2c61 | 250 | if (swapout_maxsize == 0) { |
62e76326 | 251 | if (e->store_status == STORE_OK) |
252 | storeSwapOutFileClose(e); | |
253 | ||
254 | return; /* Nevermore! */ | |
1b72bda1 | 255 | } |
62e76326 | 256 | |
c47511fd | 257 | if (e->store_status == STORE_PENDING) { |
62e76326 | 258 | /* wait for a full block to write */ |
259 | ||
260 | if (swapout_maxsize < SM_PAGE_SIZE) | |
261 | return; | |
262 | ||
263 | /* | |
264 | * Wait until we are below the disk FD limit, only if the | |
265 | * next server-side read won't be deferred. | |
266 | */ | |
a46d2c0e | 267 | if (storeTooManyDiskFilesOpen() && !e->checkDeferRead(-1)) |
62e76326 | 268 | return; |
c47511fd | 269 | } |
62e76326 | 270 | |
2391a162 | 271 | /* Ok, we have stuff to swap out. Is there a swapout.sio open? */ |
f09f5b26 | 272 | if (e->swap_status == SWAPOUT_NONE) { |
62e76326 | 273 | assert(mem->swapout.sio == NULL); |
274 | assert(mem->inmem_lo == 0); | |
275 | ||
276 | if (storeCheckCachable(e)) | |
277 | storeSwapOutStart(e); | |
278 | else | |
279 | return; | |
280 | ||
281 | /* ENTRY_CACHABLE will be cleared and we'll never get here again */ | |
f09f5b26 | 282 | } |
62e76326 | 283 | |
d3b3ab85 | 284 | if (mem->swapout.sio == NULL) |
62e76326 | 285 | return; |
286 | ||
528b2c61 | 287 | doPages(e); |
62e76326 | 288 | |
528b2c61 | 289 | if (NULL == mem->swapout.sio.getRaw()) |
62e76326 | 290 | /* oops, we're not swapping out any more */ |
291 | return; | |
292 | ||
1792fbdb | 293 | if (e->store_status == STORE_OK) { |
62e76326 | 294 | /* |
295 | * If the state is STORE_OK, then all data must have been given | |
296 | * to the filesystem at this point because storeSwapOut() is | |
297 | * not going to be called again for this entry. | |
298 | */ | |
299 | assert(mem->endOffset() == mem->swapout.queue_offset); | |
300 | storeSwapOutFileClose(e); | |
1792fbdb | 301 | } |
f09f5b26 | 302 | } |
303 | ||
304 | void | |
305 | storeSwapOutFileClose(StoreEntry * e) | |
306 | { | |
307 | MemObject *mem = e->mem_obj; | |
4215b049 | 308 | assert(mem != NULL); |
332dafa2 | 309 | debug(20, 3) ("storeSwapOutFileClose: %s\n", e->getMD5Text()); |
d3b3ab85 | 310 | debug(20, 3) ("storeSwapOutFileClose: sio = %p\n", mem->swapout.sio.getRaw()); |
62e76326 | 311 | |
2391a162 | 312 | if (mem->swapout.sio == NULL) |
62e76326 | 313 | return; |
314 | ||
2391a162 | 315 | storeClose(mem->swapout.sio); |
f09f5b26 | 316 | } |
317 | ||
318 | static void | |
e877aaac | 319 | storeSwapOutFileClosed(void *data, int errflag) |
f09f5b26 | 320 | { |
e6ccf245 | 321 | generic_cbdata *c = (generic_cbdata *)data; |
322 | StoreEntry *e = (StoreEntry *)c->data; | |
25354045 | 323 | MemObject *mem = e->mem_obj; |
2391a162 | 324 | assert(e->swap_status == SWAPOUT_WRITING); |
325 | cbdataFree(c); | |
62e76326 | 326 | |
2391a162 | 327 | if (errflag) { |
62e76326 | 328 | debug(20, 1) ("storeSwapOutFileClosed: dirno %d, swapfile %08X, errflag=%d\n\t%s\n", |
329 | e->swap_dirn, e->swap_filen, errflag, xstrerror()); | |
330 | ||
331 | if (errflag == DISK_NO_SPACE_LEFT) { | |
c8f4eac4 | 332 | /* FIXME: this should be handle by the link from store IO to |
333 | * Store, rather than being a top level API call. | |
334 | */ | |
335 | e->store()->diskFull(); | |
62e76326 | 336 | storeConfigure(); |
337 | } | |
338 | ||
339 | if (e->swap_filen > 0) | |
c8f4eac4 | 340 | e->unlink(); |
62e76326 | 341 | |
342 | e->swap_filen = -1; | |
343 | ||
344 | e->swap_dirn = -1; | |
345 | ||
346 | e->swap_status = SWAPOUT_NONE; | |
347 | ||
348 | storeReleaseRequest(e); | |
2391a162 | 349 | } else { |
62e76326 | 350 | /* swapping complete */ |
351 | debug(20, 3) ("storeSwapOutFileClosed: SwapOut complete: '%s' to %d, %08X\n", | |
352 | storeUrl(e), e->swap_dirn, e->swap_filen); | |
353 | e->swap_file_sz = objectLen(e) + mem->swap_hdr_sz; | |
354 | e->swap_status = SWAPOUT_DONE; | |
c8f4eac4 | 355 | e->store()->updateSize(e->swap_file_sz, 1); |
62e76326 | 356 | |
357 | if (storeCheckCachable(e)) { | |
358 | storeLog(STORE_LOG_SWAPOUT, e); | |
359 | storeDirSwapLog(e, SWAP_LOG_ADD); | |
360 | } | |
361 | ||
362 | statCounter.swap.outs++; | |
f09f5b26 | 363 | } |
62e76326 | 364 | |
5bd1abac | 365 | debug(20, 3) ("storeSwapOutFileClosed: %s:%d\n", __FILE__, __LINE__); |
d3b3ab85 | 366 | mem->swapout.sio = NULL; |
97b5e68f | 367 | e->unlock(); |
f09f5b26 | 368 | } |
61038223 | 369 | |
c2725718 | 370 | /* |
371 | * Is this entry a candidate for writing to disk? | |
372 | */ | |
5f25e839 | 373 | int |
c2725718 | 374 | storeSwapOutAble(const StoreEntry * e) |
375 | { | |
06d2839d | 376 | dlink_node *node; |
62e76326 | 377 | |
d3b3ab85 | 378 | if (e->mem_obj->swapout.sio.getRaw() != NULL) |
62e76326 | 379 | return 1; |
380 | ||
c2725718 | 381 | if (e->mem_obj->inmem_lo > 0) |
62e76326 | 382 | return 0; |
383 | ||
5f25e839 | 384 | /* |
385 | * If there are DISK clients, we must write to disk | |
386 | * even if its not cachable | |
528b2c61 | 387 | * RBC: Surely we should not create disk client on non cacheable objects? |
388 | * therefore this should be an assert? | |
4e70dae3 | 389 | * RBC 20030708: We can use disk to avoid mem races, so this shouldn't be |
390 | * an assert. | |
5f25e839 | 391 | */ |
06d2839d | 392 | for (node = e->mem_obj->clients.head; node; node = node->next) { |
62e76326 | 393 | if (((store_client *) node->data)->getType() == STORE_DISK_CLIENT) |
394 | return 1; | |
06d2839d | 395 | } |
62e76326 | 396 | |
a56b2212 | 397 | /* Don't pollute the disk with icons and other special entries */ |
398 | if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) | |
62e76326 | 399 | return 0; |
400 | ||
528b2c61 | 401 | if (!EBIT_TEST(e->flags, ENTRY_CACHABLE)) |
62e76326 | 402 | return 0; |
403 | ||
528b2c61 | 404 | if (!e->mem_obj->isContiguous()) |
62e76326 | 405 | return 0; |
406 | ||
528b2c61 | 407 | return 1; |
c2725718 | 408 | } |