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