]>
Commit | Line | Data |
---|---|---|
f1dc9b30 | 1 | |
2 | /* | |
cd748f27 | 3 | * $Id: store_dir.cc,v 1.107 2000/05/03 17:15:43 adrian Exp $ |
f1dc9b30 | 4 | * |
5 | * DEBUG: section 47 Store Directory Routines | |
6 | * AUTHOR: Duane Wessels | |
7 | * | |
8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ | |
e25c139f | 9 | * ---------------------------------------------------------- |
f1dc9b30 | 10 | * |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
e25c139f | 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. | |
f1dc9b30 | 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 | |
cbdec147 | 32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 33 | * |
f1dc9b30 | 34 | */ |
35 | ||
596dddc1 | 36 | #include "squid.h" |
85407535 | 37 | |
cd748f27 | 38 | static int storeDirValidSwapDirSize(int, size_t); |
d141c677 | 39 | static void storeDirLRUWalkInitHead(SwapDir * sd); |
40 | static void *storeDirLRUWalkNext(SwapDir * sd); | |
41 | ||
9838d6c8 | 42 | void |
43 | storeDirInit(void) | |
596dddc1 | 44 | { |
b2c141d4 | 45 | int i; |
46 | SwapDir *sd; | |
47 | for (i = 0; i < Config.cacheSwap.n_configured; i++) { | |
48 | sd = &Config.cacheSwap.swapDirs[i]; | |
49 | sd->init(sd); | |
50 | } | |
85407535 | 51 | } |
52 | ||
53 | void | |
54 | storeCreateSwapDirectories(void) | |
596dddc1 | 55 | { |
b2c141d4 | 56 | int i; |
57 | SwapDir *sd; | |
58 | pid_t pid; | |
59 | int status; | |
60 | for (i = 0; i < Config.cacheSwap.n_configured; i++) { | |
61 | if (fork()) | |
62 | continue; | |
63 | sd = &Config.cacheSwap.swapDirs[i]; | |
64 | sd->newfs(sd); | |
65 | exit(0); | |
66 | } | |
67 | do { | |
68 | #ifdef _SQUID_NEXT_ | |
69 | pid = wait3(&status, WNOHANG, NULL); | |
70 | #else | |
71 | pid = waitpid(-1, &status, 0); | |
72 | #endif | |
73 | } while (pid > 0 || (pid < 0 && errno == EINTR)); | |
596dddc1 | 74 | } |
75 | ||
cd748f27 | 76 | /* |
77 | * Determine whether the given directory can handle this object | |
78 | * size | |
79 | * | |
80 | * Note: if the object size is -1, then the only swapdirs that | |
81 | * will return true here are ones that have max_obj_size = -1, | |
82 | * ie any-sized-object swapdirs. This is a good thing. | |
83 | */ | |
84 | static int | |
85 | storeDirValidSwapDirSize(int swapdir, size_t objsize) | |
86 | { | |
87 | /* | |
88 | * If the swapdir's max_obj_size is -1, then it definitely can | |
89 | */ | |
90 | if (Config.cacheSwap.swapDirs[swapdir].max_objsize == -1) | |
91 | return 1; | |
92 | /* | |
93 | * Else, make sure that the max object size is larger than objsize | |
94 | */ | |
95 | if (Config.cacheSwap.swapDirs[swapdir].max_objsize > objsize) | |
96 | return 1; | |
97 | else | |
98 | return 0; | |
99 | } | |
100 | ||
101 | ||
102 | #if UNUSED /* Squid-2..4.DEVEL3 code */ | |
d141c677 | 103 | /* |
104 | * This new selection scheme simply does round-robin on all SwapDirs. | |
105 | * A SwapDir is skipped if it is over the max_size (100%) limit. If | |
106 | * all SwapDir's are above the limit, then the first dirn that we | |
107 | * checked is returned. Note that 'dirn' is guaranteed to advance even | |
108 | * if all SwapDirs are full. | |
109 | * | |
110 | * XXX This function does NOT account for the read_only flag! | |
111 | */ | |
112 | static int | |
113 | storeDirSelectSwapDir(void) | |
114 | { | |
115 | static int dirn = 0; | |
116 | int i; | |
117 | SwapDir *sd; | |
118 | /* | |
119 | * yes, the '<=' is intentional. If all dirs are full we want to | |
120 | * make sure 'dirn' advances every time this gets called, otherwise | |
121 | * we get stuck on one dir. | |
122 | */ | |
123 | for (i = 0; i <= Config.cacheSwap.n_configured; i++) { | |
124 | if (++dirn >= Config.cacheSwap.n_configured) | |
125 | dirn = 0; | |
126 | sd = &Config.cacheSwap.swapDirs[dirn]; | |
127 | if (sd->cur_size > sd->max_size) | |
128 | continue; | |
129 | return dirn; | |
130 | } | |
131 | return dirn; | |
132 | } | |
960a01e3 | 133 | |
d141c677 | 134 | #if USE_DISKD && EXPERIMENTAL |
135 | /* | |
136 | * This fileno selection function returns a fileno on the least | |
137 | * busy SwapDir. Ties are broken by selecting the SwapDir with | |
138 | * the most free space. | |
139 | */ | |
5a5c22d3 | 140 | static int |
141 | storeDirSelectSwapDir(void) | |
142 | { | |
143 | SwapDir *SD; | |
144 | int min_away = 10000; | |
d141c677 | 145 | int min_size = 1 << 30; |
5a5c22d3 | 146 | int dirn = 0; |
147 | int i; | |
148 | for (i = 0; i < Config.cacheSwap.n_configured; i++) { | |
149 | SD = &Config.cacheSwap.swapDirs[i]; | |
150 | if (SD->cur_size > SD->max_size) | |
151 | continue; | |
152 | if (SD->u.diskd.away > min_away) | |
153 | continue; | |
154 | if (SD->cur_size > min_size) | |
155 | continue; | |
156 | if (SD->flags.read_only) | |
157 | continue; | |
158 | min_away = SD->u.diskd.away; | |
159 | min_size = SD->cur_size; | |
160 | dirn = i; | |
161 | } | |
162 | return dirn; | |
163 | } | |
d141c677 | 164 | #endif |
5a5c22d3 | 165 | |
cd748f27 | 166 | #endif /* Squid-2.4.DEVEL3 code */ |
167 | ||
a2899918 | 168 | /* |
cd748f27 | 169 | * Spread load across all of the store directories |
170 | * | |
171 | * Note: We should modify this later on to prefer sticking objects | |
172 | * in the *tightest fit* swapdir to conserve space, along with the | |
173 | * actual swapdir usage. But for now, this hack will do while | |
174 | * testing, so you should order your swapdirs in the config file | |
175 | * from smallest maxobjsize to unlimited (-1) maxobjsize. | |
176 | * | |
177 | * We also have to choose nleast == nconf since we need to consider | |
178 | * ALL swapdirs, regardless of state. Again, this is a hack while | |
179 | * we sort out the real usefulness of this algorithm. | |
a2899918 | 180 | */ |
cd748f27 | 181 | int |
182 | storeDirSelectSwapDir(const StoreEntry *e) | |
a2899918 | 183 | { |
cd748f27 | 184 | size_t objsize; |
185 | size_t least_size; | |
186 | size_t least_objsize; | |
187 | int least_load = 1000; | |
188 | int load; | |
189 | int dirn = -1; | |
190 | int i; | |
a2899918 | 191 | SwapDir *SD; |
cd748f27 | 192 | |
193 | /* Calculate the object size */ | |
194 | objsize = objectLen(e); | |
195 | if (objsize != -1) | |
196 | objsize += e->mem_obj->swap_hdr_sz; | |
197 | /* Initial defaults */ | |
198 | least_size = Config.cacheSwap.swapDirs[0].cur_size; | |
199 | least_objsize = Config.cacheSwap.swapDirs[0].max_objsize; | |
200 | for (i = 0; i < Config.cacheSwap.n_configured; i++) { | |
201 | SD = &Config.cacheSwap.swapDirs[i]; | |
72615e4a | 202 | SD->flags.selected = 0; |
cd748f27 | 203 | if (SD->flags.read_only) |
204 | continue; | |
205 | /* Valid for object size check */ | |
206 | if (!storeDirValidSwapDirSize(i, objsize)) | |
207 | continue; | |
208 | load = SD->checkobj(SD, e); | |
209 | if (load < 0) | |
72615e4a | 210 | continue; |
cd748f27 | 211 | if (SD->cur_size > SD->max_size) |
212 | continue; | |
213 | if (load > least_load) | |
214 | continue; | |
215 | if ((least_objsize > 0) && (objsize > least_objsize)) | |
216 | continue; | |
217 | /* Only use leastsize if the load is equal */ | |
218 | if ((load == least_load) && (SD->cur_size > least_size)) | |
219 | continue; | |
220 | least_load = load; | |
221 | least_size = SD->cur_size; | |
222 | dirn = i; | |
a2899918 | 223 | } |
596dddc1 | 224 | |
cd748f27 | 225 | if (dirn >= 0) |
226 | Config.cacheSwap.swapDirs[dirn].flags.selected = 1; | |
0c04c389 | 227 | |
cd748f27 | 228 | return dirn; |
596dddc1 | 229 | } |
230 | ||
596dddc1 | 231 | |
596dddc1 | 232 | |
233 | char * | |
234 | storeSwapDir(int dirn) | |
235 | { | |
6d80d9a6 | 236 | assert(0 <= dirn && dirn < Config.cacheSwap.n_configured); |
f1dc9b30 | 237 | return Config.cacheSwap.swapDirs[dirn].path; |
596dddc1 | 238 | } |
4683e377 | 239 | |
b109de6b | 240 | /* |
241 | * An entry written to the swap log MUST have the following | |
242 | * properties. | |
243 | * 1. It MUST be a public key. It does no good to log | |
244 | * a public ADD, change the key, then log a private | |
245 | * DEL. So we need to log a DEL before we change a | |
246 | * key from public to private. | |
cd748f27 | 247 | * 2. It MUST have a valid (> -1) swap_filen. |
b109de6b | 248 | */ |
4683e377 | 249 | void |
5830cdb3 | 250 | storeDirSwapLog(const StoreEntry * e, int op) |
4683e377 | 251 | { |
1ffc4484 | 252 | SwapDir *sd; |
d46a87a8 | 253 | assert(!EBIT_TEST(e->flags, KEY_PRIVATE)); |
cd748f27 | 254 | assert(e->swap_filen >= 0); |
6c57e268 | 255 | /* |
256 | * icons and such; don't write them to the swap log | |
257 | */ | |
d46a87a8 | 258 | if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) |
6c57e268 | 259 | return; |
b109de6b | 260 | assert(op > SWAP_LOG_NOP && op < SWAP_LOG_MAX); |
cd748f27 | 261 | debug(20, 3) ("storeDirSwapLog: %s %s %d %08X\n", |
b109de6b | 262 | swap_log_op_str[op], |
07304bf9 | 263 | storeKeyText(e->key), |
cd748f27 | 264 | e->swap_dirn, |
265 | e->swap_filen); | |
266 | sd = &Config.cacheSwap.swapDirs[e->swap_dirn]; | |
200ba06e | 267 | sd->log.write(sd, e, op); |
5608850b | 268 | } |
76fefb77 | 269 | |
270 | void | |
cd748f27 | 271 | storeDirUpdateSwapSize(SwapDir *SD, size_t size, int sign) |
76fefb77 | 272 | { |
c932b107 | 273 | int k = ((size + 1023) >> 10) * sign; |
cd748f27 | 274 | SD->cur_size += k; |
76fefb77 | 275 | store_swap_size += k; |
0faf70d0 | 276 | if (sign > 0) |
277 | n_disk_objects++; | |
278 | else if (sign < 0) | |
279 | n_disk_objects--; | |
76fefb77 | 280 | } |
c932b107 | 281 | |
282 | void | |
3e347513 | 283 | storeDirStats(StoreEntry * sentry) |
c932b107 | 284 | { |
cd748f27 | 285 | int i; |
286 | SwapDir *SD; | |
287 | ||
c932b107 | 288 | storeAppendPrintf(sentry, "Store Directory Statistics:\n"); |
3f6c0fb2 | 289 | storeAppendPrintf(sentry, "Store Entries : %d\n", |
290 | memInUse(MEM_STOREENTRY)); | |
291 | storeAppendPrintf(sentry, "Maximum Swap Size : %8d KB\n", | |
292 | Config.Swap.maxSize); | |
293 | storeAppendPrintf(sentry, "Current Store Swap Size: %8d KB\n", | |
294 | store_swap_size); | |
dd8037ab | 295 | storeAppendPrintf(sentry, "Current Capacity : %d%% used, %d%% free\n", |
296 | percent((int) store_swap_size, (int) Config.Swap.maxSize), | |
38d04788 | 297 | percent((int) (Config.Swap.maxSize - store_swap_size), (int) Config.Swap.maxSize)); |
e3ef2b09 | 298 | |
cd748f27 | 299 | /* Now go through each swapdir, calling its statfs routine */ |
300 | for (i = 0; i < Config.cacheSwap.n_configured; i++) { | |
301 | storeAppendPrintf(sentry, "\n"); | |
302 | SD = &(Config.cacheSwap.swapDirs[i]); | |
303 | storeAppendPrintf(sentry, "Store Directory #%d (%s): %s\n", i, SD->type, | |
304 | storeSwapDir(i)); | |
305 | SD->statfs(SD, sentry); | |
306 | } | |
5d406e78 | 307 | } |
308 | ||
f4e3fa54 | 309 | void |
310 | storeDirConfigure(void) | |
311 | { | |
312 | SwapDir *SD; | |
f4e3fa54 | 313 | int i; |
f4e3fa54 | 314 | Config.Swap.maxSize = 0; |
315 | for (i = 0; i < Config.cacheSwap.n_configured; i++) { | |
5942e8d4 | 316 | SD = &Config.cacheSwap.swapDirs[i];; |
317 | Config.Swap.maxSize += SD->max_size; | |
c6844e77 | 318 | SD->low_size = (int) (((float) SD->max_size * |
319 | (float) Config.Swap.lowWaterMark) / 100.0); | |
f4e3fa54 | 320 | } |
321 | } | |
322 | ||
323 | void | |
cd748f27 | 324 | storeDirDiskFull(sdirno dirn) |
f4e3fa54 | 325 | { |
f4e3fa54 | 326 | SwapDir *SD = &Config.cacheSwap.swapDirs[dirn]; |
327 | assert(0 <= dirn && dirn < Config.cacheSwap.n_configured); | |
328 | SD->max_size = SD->cur_size; | |
5942e8d4 | 329 | debug(20, 1) ("WARNING: Shrinking cache_dir #%d to %d KB\n", |
f4e3fa54 | 330 | dirn, SD->cur_size); |
331 | } | |
95dcd2b8 | 332 | |
333 | void | |
334 | storeDirOpenSwapLogs(void) | |
335 | { | |
b2c141d4 | 336 | int dirn; |
337 | SwapDir *sd; | |
338 | for (dirn = 0; dirn < Config.cacheSwap.n_configured; dirn++) { | |
339 | sd = &Config.cacheSwap.swapDirs[dirn]; | |
340 | sd->log.open(sd); | |
341 | } | |
95dcd2b8 | 342 | } |
343 | ||
344 | void | |
345 | storeDirCloseSwapLogs(void) | |
346 | { | |
b2c141d4 | 347 | int dirn; |
348 | SwapDir *sd; | |
349 | for (dirn = 0; dirn < Config.cacheSwap.n_configured; dirn++) { | |
350 | sd = &Config.cacheSwap.swapDirs[dirn]; | |
351 | sd->log.close(sd); | |
352 | } | |
95dcd2b8 | 353 | } |
354 | ||
b2c141d4 | 355 | /* |
356 | * storeDirWriteCleanLogs | |
357 | * | |
358 | * Writes a "clean" swap log file from in-memory metadata. | |
cd748f27 | 359 | * This is a rewrite of the original function to troll each |
360 | * StoreDir and write the logs, and flush at the end of | |
361 | * the run. Thanks goes to Eric Stern, since this solution | |
362 | * came out of his COSS code. | |
b2c141d4 | 363 | */ |
364 | #define CLEAN_BUF_SZ 16384 | |
365 | int | |
366 | storeDirWriteCleanLogs(int reopen) | |
95dcd2b8 | 367 | { |
b2c141d4 | 368 | StoreEntry *e = NULL; |
369 | int n = 0; | |
e812ecfc | 370 | struct timeval start; |
371 | double dt; | |
b2c141d4 | 372 | SwapDir *sd; |
373 | int dirn; | |
2b906e48 | 374 | #if HEAP_REPLACEMENT |
375 | int node; | |
2b906e48 | 376 | #endif |
b2c141d4 | 377 | if (store_dirs_rebuilding) { |
378 | debug(20, 1) ("Not currently OK to rewrite swap log.\n"); | |
379 | debug(20, 1) ("storeDirWriteCleanLogs: Operation aborted.\n"); | |
380 | return 0; | |
381 | } | |
382 | debug(20, 1) ("storeDirWriteCleanLogs: Starting...\n"); | |
e812ecfc | 383 | getCurrentTime(); |
384 | start = current_time; | |
b2c141d4 | 385 | for (dirn = 0; dirn < Config.cacheSwap.n_configured; dirn++) { |
386 | sd = &Config.cacheSwap.swapDirs[dirn]; | |
387 | if (sd->log.clean.open(sd) < 0) { | |
388 | debug(20, 1) ("log.clean.open() failed for dir #%d\n", sd->index); | |
389 | continue; | |
390 | } | |
b2c141d4 | 391 | if (NULL == sd->log.clean.write) |
392 | continue; | |
cd748f27 | 393 | #if HEAP_REPLACEMENT |
394 | if (NULL == sd->repl.heap.heap) | |
395 | continue; | |
396 | #endif | |
397 | #if HEAP_REPLACEMENT | |
398 | for (node = 0; node < heap_nodes(sd->repl.heap.heap); node++) | |
d141c677 | 399 | #else |
cd748f27 | 400 | storeDirLRUWalkInitHead(sd); |
401 | while ((e = storeDirLRUWalkNext(sd)) != NULL) | |
402 | #endif | |
403 | { | |
404 | #if HEAP_REPLACEMENT | |
405 | e = (StoreEntry *) heap_peep(sd->repl.heap.heap, node); | |
406 | #endif | |
407 | if (e->swap_filen < 0) | |
d141c677 | 408 | continue; |
409 | if (e->swap_status != SWAPOUT_DONE) | |
410 | continue; | |
411 | if (e->swap_file_sz <= 0) | |
412 | continue; | |
413 | if (EBIT_TEST(e->flags, RELEASE_REQUEST)) | |
414 | continue; | |
415 | if (EBIT_TEST(e->flags, KEY_PRIVATE)) | |
416 | continue; | |
417 | if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) | |
418 | continue; | |
419 | sd->log.clean.write(e, sd); | |
420 | if ((++n & 0xFFFF) == 0) { | |
421 | getCurrentTime(); | |
422 | debug(20, 1) (" %7d entries written so far.\n", n); | |
423 | } | |
424 | } | |
cd748f27 | 425 | /* Flush */ |
426 | sd->log.clean.write(NULL, sd); | |
427 | } | |
b2c141d4 | 428 | if (reopen) |
429 | storeDirOpenSwapLogs(); | |
e812ecfc | 430 | getCurrentTime(); |
431 | dt = tvSubDsec(start, current_time); | |
b2c141d4 | 432 | debug(20, 1) (" Finished. Wrote %d entries.\n", n); |
e812ecfc | 433 | debug(20, 1) (" Took %3.1f seconds (%6.1f entries/sec).\n", |
434 | dt, (double) n / (dt > 0.0 ? dt : 1.0)); | |
b2c141d4 | 435 | return n; |
95dcd2b8 | 436 | } |
b2c141d4 | 437 | #undef CLEAN_BUF_SZ |
d141c677 | 438 | |
cd748f27 | 439 | /* |
440 | * sync all avaliable fs'es .. | |
441 | */ | |
442 | void | |
443 | storeDirSync(void) | |
444 | { | |
445 | int i; | |
446 | SwapDir *SD; | |
447 | ||
448 | for (i = 0; i < Config.cacheSwap.n_configured; i++) { | |
449 | SD = &Config.cacheSwap.swapDirs[i]; | |
450 | if (SD->sync != NULL) | |
451 | SD->sync(SD); | |
452 | } | |
453 | } | |
454 | ||
455 | /* | |
456 | * handle callbacks all avaliable fs'es .. | |
457 | */ | |
458 | void | |
459 | storeDirCallback(void) | |
460 | { | |
461 | int i; | |
462 | SwapDir *SD; | |
463 | ||
464 | for (i = 0; i < Config.cacheSwap.n_configured; i++) { | |
465 | SD = &Config.cacheSwap.swapDirs[i]; | |
466 | if (SD->callback != NULL) | |
467 | SD->callback(SD); | |
468 | } | |
469 | } | |
470 | ||
471 | #if 0 /* from Squid-2.4.DEVEL3 */ | |
d141c677 | 472 | void |
473 | storeDirLRUDelete(StoreEntry * e) | |
474 | { | |
475 | SwapDir *sd; | |
cd748f27 | 476 | if (e->swap_filen < 0) |
d141c677 | 477 | return; |
cd748f27 | 478 | sd = &Config.cacheSwap.swapDirs[e->swap_dirn]; |
479 | dlinkDelete(&e->lru, &sd->repl.lru.list); | |
d141c677 | 480 | } |
481 | ||
482 | void | |
483 | storeDirLRUAdd(StoreEntry * e) | |
484 | { | |
485 | SwapDir *sd; | |
486 | if (e->swap_file_number < 0) | |
487 | return; | |
cd748f27 | 488 | sd = &Config.cacheSwap.swapDirs[e->swap_dirn]; |
489 | dlinkAdd(e, &e->lru, &sd->repl.lru.list); | |
d141c677 | 490 | } |
cd748f27 | 491 | #endif /* from Squid-2.4.DEVEL3 */ |
d141c677 | 492 | |
493 | static void | |
494 | storeDirLRUWalkInitHead(SwapDir * sd) | |
495 | { | |
cd748f27 | 496 | sd->repl.lru.walker = sd->repl.lru.list.head; |
d141c677 | 497 | } |
498 | ||
499 | static void * | |
500 | storeDirLRUWalkNext(SwapDir * sd) | |
501 | { | |
502 | void *p; | |
cd748f27 | 503 | if (NULL == sd->repl.lru.walker) |
d141c677 | 504 | return NULL; |
cd748f27 | 505 | p = sd->repl.lru.walker->data; |
506 | sd->repl.lru.walker = sd->repl.lru.walker->next; | |
d141c677 | 507 | return p; |
508 | } |