]>
Commit | Line | Data |
---|---|---|
9cef6668 | 1 | |
2 | /* | |
50a49a6f | 3 | * $Id: store_swapout.cc,v 1.64 2000/04/18 06:06:17 wessels Exp $ |
9cef6668 | 4 | * |
5 | * DEBUG: section 20 Storage Manager Swapout Functions | |
6 | * AUTHOR: Duane Wessels | |
7 | * | |
8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ | |
9 | * ---------------------------------------------------------- | |
10 | * | |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
13 | * National Laboratory for Applied Network Research and funded by the | |
14 | * National Science Foundation. Squid is Copyrighted (C) 1998 by | |
efd900cb | 15 | * the Regents of the University of California. Please see the |
16 | * COPYRIGHT file for full details. Squid incorporates software | |
17 | * developed and/or copyrighted by other sources. Please see the | |
18 | * 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" |
37 | ||
bbcd7374 | 38 | static off_t storeSwapOutObjectBytesOnDisk(const MemObject *); |
d54f0ab3 | 39 | static void storeSwapOutStart(StoreEntry * e); |
2391a162 | 40 | static STIOCB storeSwapOutFileClosed; |
f09f5b26 | 41 | |
42 | /* start swapping object to disk */ | |
d54f0ab3 | 43 | static void |
f09f5b26 | 44 | storeSwapOutStart(StoreEntry * e) |
45 | { | |
2391a162 | 46 | generic_cbdata *c; |
47 | MemObject *mem = e->mem_obj; | |
48 | int swap_hdr_sz = 0; | |
49 | tlv *tlv_list; | |
50 | char *buf; | |
51 | assert(mem); | |
f09f5b26 | 52 | storeLockObject(e); |
6ba94df3 | 53 | storeSwapFileNumberSet(e, storeDirMapAllocate()); |
2391a162 | 54 | c = xcalloc(1, sizeof(*c)); |
55 | c->data = e; | |
56 | cbdataAdd(c, cbdataXfree, 0); | |
57 | mem->swapout.sio = storeOpen(e->swap_file_number, | |
58 | O_WRONLY, storeSwapOutFileClosed, c); | |
3fc29499 | 59 | if (NULL == mem->swapout.sio) { |
60 | e->swap_status = SWAPOUT_NONE; | |
6ba94df3 | 61 | storeSwapFileNumberSet(e, -1); |
3fc29499 | 62 | cbdataFree(c); |
63 | storeUnlockObject(e); | |
64 | return; | |
65 | } | |
2391a162 | 66 | e->swap_status = SWAPOUT_WRITING; |
3fc29499 | 67 | cbdataLock(mem->swapout.sio); |
2391a162 | 68 | debug(20, 5) ("storeSwapOutStart: Begin SwapOut '%s' to fileno %08X\n", |
69 | storeUrl(e), e->swap_file_number); | |
70 | tlv_list = storeSwapMetaBuild(e); | |
71 | buf = storeSwapMetaPack(tlv_list, &swap_hdr_sz); | |
72 | storeSwapTLVFree(tlv_list); | |
73 | mem->swap_hdr_sz = (size_t) swap_hdr_sz; | |
50f4d6ae | 74 | storeWrite(mem->swapout.sio, buf, mem->swap_hdr_sz, 0, xfree); |
f09f5b26 | 75 | } |
76 | ||
77 | void | |
2391a162 | 78 | storeSwapOut(StoreEntry * e) |
f09f5b26 | 79 | { |
80 | MemObject *mem = e->mem_obj; | |
81 | off_t lowest_offset; | |
82 | off_t new_mem_lo; | |
2391a162 | 83 | off_t on_disk = 0; |
f09f5b26 | 84 | size_t swapout_size; |
85 | char *swap_buf; | |
86 | ssize_t swap_buf_len; | |
77b32a34 | 87 | if (mem == NULL) |
88 | return; | |
f09f5b26 | 89 | /* should we swap something out to disk? */ |
2391a162 | 90 | debug(20, 7) ("storeSwapOut: %s\n", storeUrl(e)); |
91 | debug(20, 7) ("storeSwapOut: store_status = %s\n", | |
f09f5b26 | 92 | storeStatusStr[e->store_status]); |
b7fe0ab0 | 93 | if (EBIT_TEST(e->flags, ENTRY_ABORTED)) { |
d46a87a8 | 94 | assert(EBIT_TEST(e->flags, RELEASE_REQUEST)); |
f09f5b26 | 95 | storeSwapOutFileClose(e); |
96 | return; | |
97 | } | |
2391a162 | 98 | debug(20, 7) ("storeSwapOut: mem->inmem_lo = %d\n", |
f09f5b26 | 99 | (int) mem->inmem_lo); |
2391a162 | 100 | debug(20, 7) ("storeSwapOut: mem->inmem_hi = %d\n", |
f09f5b26 | 101 | (int) mem->inmem_hi); |
2391a162 | 102 | debug(20, 7) ("storeSwapOut: swapout.queue_offset = %d\n", |
f09f5b26 | 103 | (int) mem->swapout.queue_offset); |
2391a162 | 104 | if (mem->swapout.sio) |
60df005c | 105 | debug(20, 7) ("storeSwapOut: storeOffset() = %d\n", |
2391a162 | 106 | (int) storeOffset(mem->swapout.sio)); |
f09f5b26 | 107 | assert(mem->inmem_hi >= mem->swapout.queue_offset); |
f09f5b26 | 108 | lowest_offset = storeLowestMemReaderOffset(e); |
2391a162 | 109 | debug(20, 7) ("storeSwapOut: lowest_offset = %d\n", |
f09f5b26 | 110 | (int) lowest_offset); |
50a49a6f | 111 | /* |
112 | * Careful. lowest_offset can be greater than inmem_hi, such | |
113 | * as in the case of a range request. | |
114 | */ | |
115 | if (mem->inmem_hi < lowest_offset) | |
116 | new_mem_lo = lowest_offset; | |
117 | else if (mem->inmem_hi - lowest_offset > DISK_PAGE_SIZE) | |
118 | new_mem_lo = lowest_offset; | |
279447ff | 119 | else |
50a49a6f | 120 | new_mem_lo = mem->inmem_lo; |
c2725718 | 121 | assert(new_mem_lo >= mem->inmem_lo); |
eb824054 | 122 | if (storeSwapOutAble(e)) { |
1f38f50a | 123 | /* |
124 | * We should only free up to what we know has been written | |
125 | * to disk, not what has been queued for writing. Otherwise | |
126 | * there will be a chunk of the data which is not in memory | |
127 | * and is not yet on disk. | |
128 | */ | |
1b72bda1 | 129 | if ((on_disk = storeSwapOutObjectBytesOnDisk(mem)) < new_mem_lo) |
c2725718 | 130 | new_mem_lo = on_disk; |
279447ff | 131 | } else if (new_mem_lo > 0) { |
1f38f50a | 132 | /* |
279447ff | 133 | * Its not swap-able, and we're about to delete a chunk, |
134 | * so we must make it PRIVATE. This is tricky/ugly because | |
135 | * for the most part, we treat swapable == cachable here. | |
1f38f50a | 136 | */ |
eb824054 | 137 | storeReleaseRequest(e); |
1f38f50a | 138 | } |
18fe65d0 | 139 | stmemFreeDataUpto(&mem->data_hdr, new_mem_lo); |
f09f5b26 | 140 | mem->inmem_lo = new_mem_lo; |
c2725718 | 141 | if (e->swap_status == SWAPOUT_WRITING) |
2391a162 | 142 | assert(mem->inmem_lo <= on_disk); |
c2725718 | 143 | if (!storeSwapOutAble(e)) |
1b72bda1 | 144 | return; |
f09f5b26 | 145 | swapout_size = (size_t) (mem->inmem_hi - mem->swapout.queue_offset); |
2391a162 | 146 | debug(20, 7) ("storeSwapOut: swapout_size = %d\n", |
f09f5b26 | 147 | (int) swapout_size); |
1b72bda1 | 148 | if (swapout_size == 0) { |
614a44a6 | 149 | if (e->store_status == STORE_OK) |
1b72bda1 | 150 | storeSwapOutFileClose(e); |
614a44a6 | 151 | return; /* Nevermore! */ |
1b72bda1 | 152 | } |
c47511fd | 153 | if (e->store_status == STORE_PENDING) { |
154 | /* wait for a full block to write */ | |
614a44a6 | 155 | if (swapout_size < DISK_PAGE_SIZE) |
c47511fd | 156 | return; |
157 | /* | |
158 | * Wait until we are below the disk FD limit, only if the | |
159 | * next server-side read won't be deferred. | |
160 | */ | |
161 | if (storeTooManyDiskFilesOpen() && !fwdCheckDeferRead(-1, e)) | |
162 | return; | |
163 | } | |
2391a162 | 164 | /* Ok, we have stuff to swap out. Is there a swapout.sio open? */ |
f09f5b26 | 165 | if (e->swap_status == SWAPOUT_NONE) { |
2391a162 | 166 | assert(mem->swapout.sio == NULL); |
c2725718 | 167 | assert(mem->inmem_lo == 0); |
f09f5b26 | 168 | if (storeCheckCachable(e)) |
169 | storeSwapOutStart(e); | |
2391a162 | 170 | else |
171 | return; | |
614a44a6 | 172 | /* ENTRY_CACHABLE will be cleared and we'll never get here again */ |
f09f5b26 | 173 | } |
3fc29499 | 174 | if (NULL == mem->swapout.sio) |
175 | return; | |
614a44a6 | 176 | do { |
177 | if (swapout_size > DISK_PAGE_SIZE) | |
178 | swapout_size = DISK_PAGE_SIZE; | |
179 | swap_buf = memAllocate(MEM_DISK_BUF); | |
180 | swap_buf_len = stmemCopy(&mem->data_hdr, | |
181 | mem->swapout.queue_offset, | |
182 | swap_buf, | |
183 | swapout_size); | |
184 | if (swap_buf_len < 0) { | |
185 | debug(20, 1) ("stmemCopy returned %d for '%s'\n", swap_buf_len, storeKeyText(e->key)); | |
186 | storeUnlink(e->swap_file_number); | |
6ba94df3 | 187 | storeSwapFileNumberSet(e, -1); |
614a44a6 | 188 | e->swap_status = SWAPOUT_NONE; |
189 | memFree(swap_buf, MEM_DISK_BUF); | |
190 | storeReleaseRequest(e); | |
191 | storeSwapOutFileClose(e); | |
192 | return; | |
193 | } | |
194 | debug(20, 3) ("storeSwapOut: swap_buf_len = %d\n", (int) swap_buf_len); | |
195 | assert(swap_buf_len > 0); | |
196 | debug(20, 3) ("storeSwapOut: swapping out %d bytes from %d\n", | |
197 | swap_buf_len, (int) mem->swapout.queue_offset); | |
198 | mem->swapout.queue_offset += swap_buf_len; | |
199 | storeWrite(mem->swapout.sio, swap_buf, swap_buf_len, -1, memFreeDISK); | |
200 | /* the storeWrite() call might generate an error */ | |
201 | if (e->swap_status != SWAPOUT_WRITING) | |
202 | break; | |
203 | swapout_size = (size_t) (mem->inmem_hi - mem->swapout.queue_offset); | |
1792fbdb | 204 | if (e->store_status == STORE_PENDING) |
205 | if (swapout_size < DISK_PAGE_SIZE) | |
206 | break; | |
207 | } while (swapout_size > 0); | |
3fc29499 | 208 | if (NULL == mem->swapout.sio) |
209 | /* oops, we're not swapping out any more */ | |
210 | return; | |
1792fbdb | 211 | if (e->store_status == STORE_OK) { |
212 | /* | |
213 | * If the state is STORE_OK, then all data must have been given | |
214 | * to the filesystem at this point because storeSwapOut() is | |
215 | * not going to be called again for this entry. | |
216 | */ | |
217 | assert(mem->inmem_hi == mem->swapout.queue_offset); | |
218 | storeSwapOutFileClose(e); | |
219 | } | |
f09f5b26 | 220 | } |
221 | ||
222 | void | |
223 | storeSwapOutFileClose(StoreEntry * e) | |
224 | { | |
225 | MemObject *mem = e->mem_obj; | |
4215b049 | 226 | assert(mem != NULL); |
2ac76861 | 227 | debug(20, 3) ("storeSwapOutFileClose: %s\n", storeKeyText(e->key)); |
5bd1abac | 228 | debug(20, 3) ("storeSwapOutFileClose: sio = %p\n", mem->swapout.sio); |
2391a162 | 229 | if (mem->swapout.sio == NULL) |
25354045 | 230 | return; |
2391a162 | 231 | storeClose(mem->swapout.sio); |
f09f5b26 | 232 | } |
233 | ||
234 | static void | |
2391a162 | 235 | storeSwapOutFileClosed(void *data, int errflag, storeIOState * sio) |
f09f5b26 | 236 | { |
2391a162 | 237 | generic_cbdata *c = data; |
238 | StoreEntry *e = c->data; | |
25354045 | 239 | MemObject *mem = e->mem_obj; |
2391a162 | 240 | assert(e->swap_status == SWAPOUT_WRITING); |
241 | cbdataFree(c); | |
242 | if (errflag) { | |
6ba94df3 | 243 | sfileno bad = e->swap_file_number; |
2391a162 | 244 | debug(20, 1) ("storeSwapOutFileClosed: swapfile %08X, errflag=%d\n\t%s\n", |
6ba94df3 | 245 | bad, errflag, xstrerror()); |
246 | storeSwapFileNumberSet(e, -1); | |
b2f9d4cd | 247 | /* |
6ba94df3 | 248 | * yuck. re-set the filemap bit for some errors so that |
b2f9d4cd | 249 | * we don't try re-using it over and over |
250 | */ | |
6ba94df3 | 251 | if (errno == EPERM) |
252 | storeDirMapBitSet(bad); | |
2391a162 | 253 | if (errflag == DISK_NO_SPACE_LEFT) { |
97de0ec7 | 254 | storeDirDiskFull(bad); |
2391a162 | 255 | storeDirConfigure(); |
256 | storeConfigure(); | |
257 | } | |
5bd1abac | 258 | storeReleaseRequest(e); |
2391a162 | 259 | e->swap_status = SWAPOUT_NONE; |
2391a162 | 260 | } else { |
261 | /* swapping complete */ | |
262 | debug(20, 3) ("storeSwapOutFileClosed: SwapOut complete: '%s' to %08X\n", | |
263 | storeUrl(e), e->swap_file_number); | |
264 | e->swap_file_sz = objectLen(e) + mem->swap_hdr_sz; | |
265 | e->swap_status = SWAPOUT_DONE; | |
266 | storeDirUpdateSwapSize(e->swap_file_number, e->swap_file_sz, 1); | |
267 | if (storeCheckCachable(e)) { | |
268 | storeLog(STORE_LOG_SWAPOUT, e); | |
269 | storeDirSwapLog(e, SWAP_LOG_ADD); | |
270 | } | |
f09f5b26 | 271 | } |
5bd1abac | 272 | debug(20, 3) ("storeSwapOutFileClosed: %s:%d\n", __FILE__, __LINE__); |
273 | mem->swapout.sio = NULL; | |
2391a162 | 274 | cbdataUnlock(sio); |
5bd1abac | 275 | storeUnlockObject(e); |
f09f5b26 | 276 | } |
61038223 | 277 | |
bbcd7374 | 278 | /* |
279 | * How much of the object data is on the disk? | |
280 | */ | |
281 | static off_t | |
282 | storeSwapOutObjectBytesOnDisk(const MemObject * mem) | |
283 | { | |
284 | /* | |
2391a162 | 285 | * NOTE: storeOffset() represents the disk file size, |
bbcd7374 | 286 | * not the amount of object data on disk. |
287 | * | |
288 | * If we don't have at least 'swap_hdr_sz' bytes | |
289 | * then none of the object data is on disk. | |
290 | * | |
291 | * This should still be safe if swap_hdr_sz == 0, | |
292 | * meaning we haven't even opened the swapout file | |
293 | * yet. | |
294 | */ | |
2391a162 | 295 | off_t nwritten; |
60df005c | 296 | if (mem->swapout.sio == NULL) |
bbcd7374 | 297 | return 0; |
2391a162 | 298 | nwritten = storeOffset(mem->swapout.sio); |
299 | if (nwritten <= mem->swap_hdr_sz) | |
300 | return 0; | |
301 | return nwritten - mem->swap_hdr_sz; | |
bbcd7374 | 302 | } |
c2725718 | 303 | |
304 | /* | |
305 | * Is this entry a candidate for writing to disk? | |
306 | */ | |
5f25e839 | 307 | int |
c2725718 | 308 | storeSwapOutAble(const StoreEntry * e) |
309 | { | |
5f25e839 | 310 | store_client *sc; |
2391a162 | 311 | if (e->mem_obj->swapout.sio != NULL) |
c2725718 | 312 | return 1; |
313 | if (e->mem_obj->inmem_lo > 0) | |
314 | return 0; | |
5f25e839 | 315 | /* |
316 | * If there are DISK clients, we must write to disk | |
317 | * even if its not cachable | |
318 | */ | |
135171fe | 319 | for (sc = e->mem_obj->clients; sc; sc = sc->next) |
5f25e839 | 320 | if (sc->type == STORE_DISK_CLIENT) |
321 | return 1; | |
1792fbdb | 322 | if (store_dirs_rebuilding) |
323 | if (!EBIT_TEST(e->flags, ENTRY_SPECIAL)) | |
324 | return 0; | |
d46a87a8 | 325 | return EBIT_TEST(e->flags, ENTRY_CACHABLE); |
c2725718 | 326 | } |