]> git.ipfire.org Git - thirdparty/squid.git/blob - src/store_dir.cc
printf typedef.
[thirdparty/squid.git] / src / store_dir.cc
1
2 /*
3 * $Id: store_dir.cc,v 1.138 2002/10/14 11:57:13 adrian Exp $
4 *
5 * DEBUG: section 47 Store Directory Routines
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
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.
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
36 #include "squid.h"
37 #include "Store.h"
38
39 #if HAVE_STATVFS
40 #if HAVE_SYS_STATVFS_H
41 #include <sys/statvfs.h>
42 #endif
43 #endif
44 /* Windows uses sys/vfs.h */
45 #if HAVE_SYS_VFS_H
46 #include <sys/vfs.h>
47 #endif
48
49 static int storeDirValidSwapDirSize(int, ssize_t);
50 static STDIRSELECT storeDirSelectSwapDirRoundRobin;
51 static STDIRSELECT storeDirSelectSwapDirLeastLoad;
52
53 /*
54 * This function pointer is set according to 'store_dir_select_algorithm'
55 * in squid.conf.
56 */
57 STDIRSELECT *storeDirSelectSwapDir = storeDirSelectSwapDirLeastLoad;
58
59 void
60 storeDirInit(void)
61 {
62 int i;
63 SwapDir *sd;
64 for (i = 0; i < Config.cacheSwap.n_configured; i++) {
65 sd = &Config.cacheSwap.swapDirs[i];
66 sd->init(sd);
67 }
68 if (0 == strcasecmp(Config.store_dir_select_algorithm, "round-robin")) {
69 storeDirSelectSwapDir = storeDirSelectSwapDirRoundRobin;
70 debug(47, 1) ("Using Round Robin store dir selection\n");
71 } else {
72 storeDirSelectSwapDir = storeDirSelectSwapDirLeastLoad;
73 debug(47, 1) ("Using Least Load store dir selection\n");
74 }
75 }
76
77 void
78 storeCreateSwapDirectories(void)
79 {
80 int i;
81 SwapDir *sd;
82 pid_t pid;
83 int status;
84 for (i = 0; i < Config.cacheSwap.n_configured; i++) {
85 if (fork())
86 continue;
87 sd = &Config.cacheSwap.swapDirs[i];
88 if (NULL != sd->newfs)
89 sd->newfs(sd);
90 exit(0);
91 }
92 do {
93 #ifdef _SQUID_NEXT_
94 pid = wait3(&status, WNOHANG, NULL);
95 #else
96 pid = waitpid(-1, &status, 0);
97 #endif
98 } while (pid > 0 || (pid < 0 && errno == EINTR));
99 }
100
101 /*
102 * Determine whether the given directory can handle this object
103 * size
104 *
105 * Note: if the object size is -1, then the only swapdirs that
106 * will return true here are ones that have max_obj_size = -1,
107 * ie any-sized-object swapdirs. This is a good thing.
108 */
109 static int
110 storeDirValidSwapDirSize(int swapdir, ssize_t objsize)
111 {
112 /*
113 * If the swapdir's max_obj_size is -1, then it definitely can
114 */
115 if (Config.cacheSwap.swapDirs[swapdir].max_objsize == -1)
116 return 1;
117
118 /*
119 * If the object size is -1, then if the storedir isn't -1 we
120 * can't store it
121 */
122 if ((objsize == -1) &&
123 (Config.cacheSwap.swapDirs[swapdir].max_objsize != -1))
124 return 0;
125
126 /*
127 * Else, make sure that the max object size is larger than objsize
128 */
129 if (Config.cacheSwap.swapDirs[swapdir].max_objsize > objsize)
130 return 1;
131 else
132 return 0;
133 }
134
135
136 /*
137 * This new selection scheme simply does round-robin on all SwapDirs.
138 * A SwapDir is skipped if it is over the max_size (100%) limit, or
139 * overloaded.
140 */
141 static int
142 storeDirSelectSwapDirRoundRobin(const StoreEntry * e)
143 {
144 static int dirn = 0;
145 int i;
146 int load;
147 SwapDir *sd;
148 ssize_t objsize = (ssize_t) objectLen(e);
149 for (i = 0; i <= Config.cacheSwap.n_configured; i++) {
150 if (++dirn >= Config.cacheSwap.n_configured)
151 dirn = 0;
152 sd = &Config.cacheSwap.swapDirs[dirn];
153 if (sd->flags.read_only)
154 continue;
155 if (sd->cur_size > sd->max_size)
156 continue;
157 if (!storeDirValidSwapDirSize(i, objsize))
158 continue;
159 /* check for error or overload condition */
160 load = sd->checkobj(sd, e);
161 if (load < 0 || load > 1000) {
162 continue;
163 }
164 return dirn;
165 }
166 return -1;
167 }
168
169 /*
170 * Spread load across all of the store directories
171 *
172 * Note: We should modify this later on to prefer sticking objects
173 * in the *tightest fit* swapdir to conserve space, along with the
174 * actual swapdir usage. But for now, this hack will do while
175 * testing, so you should order your swapdirs in the config file
176 * from smallest maxobjsize to unlimited (-1) maxobjsize.
177 *
178 * We also have to choose nleast == nconf since we need to consider
179 * ALL swapdirs, regardless of state. Again, this is a hack while
180 * we sort out the real usefulness of this algorithm.
181 */
182 static int
183 storeDirSelectSwapDirLeastLoad(const StoreEntry * e)
184 {
185 ssize_t objsize;
186 ssize_t most_free = 0, cur_free;
187 ssize_t least_objsize = -1;
188 int least_load = INT_MAX;
189 int load;
190 int dirn = -1;
191 int i;
192 SwapDir *SD;
193
194 /* Calculate the object size */
195 objsize = (ssize_t) objectLen(e);
196 if (objsize != -1)
197 objsize += e->mem_obj->swap_hdr_sz;
198 for (i = 0; i < Config.cacheSwap.n_configured; i++) {
199 SD = &Config.cacheSwap.swapDirs[i];
200 SD->flags.selected = 0;
201 load = SD->checkobj(SD, e);
202 if (load < 0 || load > 1000) {
203 continue;
204 }
205 if (!storeDirValidSwapDirSize(i, objsize))
206 continue;
207 if (SD->flags.read_only)
208 continue;
209 if (SD->cur_size > SD->max_size)
210 continue;
211 if (load > least_load)
212 continue;
213 cur_free = SD->max_size - SD->cur_size;
214 /* If the load is equal, then look in more details */
215 if (load == least_load) {
216 /* closest max_objsize fit */
217 if (least_objsize != -1)
218 if (SD->max_objsize > least_objsize || SD->max_objsize == -1)
219 continue;
220 /* most free */
221 if (cur_free < most_free)
222 continue;
223 }
224 least_load = load;
225 least_objsize = SD->max_objsize;
226 most_free = cur_free;
227 dirn = i;
228 }
229 if (dirn >= 0)
230 Config.cacheSwap.swapDirs[dirn].flags.selected = 1;
231 return dirn;
232 }
233
234
235
236 char *
237 storeSwapDir(int dirn)
238 {
239 assert(0 <= dirn && dirn < Config.cacheSwap.n_configured);
240 return Config.cacheSwap.swapDirs[dirn].path;
241 }
242
243 /*
244 * An entry written to the swap log MUST have the following
245 * properties.
246 * 1. It MUST be a public key. It does no good to log
247 * a public ADD, change the key, then log a private
248 * DEL. So we need to log a DEL before we change a
249 * key from public to private.
250 * 2. It MUST have a valid (> -1) swap_filen.
251 */
252 void
253 storeDirSwapLog(const StoreEntry * e, int op)
254 {
255 SwapDir *sd;
256 assert(!EBIT_TEST(e->flags, KEY_PRIVATE));
257 assert(e->swap_filen >= 0);
258 /*
259 * icons and such; don't write them to the swap log
260 */
261 if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
262 return;
263 assert(op > SWAP_LOG_NOP && op < SWAP_LOG_MAX);
264 debug(20, 3) ("storeDirSwapLog: %s %s %d %08X\n",
265 swap_log_op_str[op],
266 storeKeyText((const cache_key *)e->hash.key),
267 e->swap_dirn,
268 e->swap_filen);
269 sd = &Config.cacheSwap.swapDirs[e->swap_dirn];
270 sd->log.write(sd, e, op);
271 }
272
273 void
274 storeDirUpdateSwapSize(SwapDir * SD, size_t size, int sign)
275 {
276 int blks = (size + SD->fs.blksize - 1) / SD->fs.blksize;
277 int k = (blks * SD->fs.blksize >> 10) * sign;
278 SD->cur_size += k;
279 store_swap_size += k;
280 if (sign > 0)
281 n_disk_objects++;
282 else if (sign < 0)
283 n_disk_objects--;
284 }
285
286 void
287 storeDirStats(StoreEntry * sentry)
288 {
289 int i;
290 SwapDir *SD;
291
292 storeAppendPrintf(sentry, "Store Directory Statistics:\n");
293 storeAppendPrintf(sentry, "Store Entries : %lu\n",
294 (unsigned long int)storeEntryInUse());
295 storeAppendPrintf(sentry, "Maximum Swap Size : %8ld KB\n",
296 (long int) Config.Swap.maxSize);
297 storeAppendPrintf(sentry, "Current Store Swap Size: %8lu KB\n",
298 store_swap_size);
299 storeAppendPrintf(sentry, "Current Capacity : %d%% used, %d%% free\n",
300 percent((int) store_swap_size, (int) Config.Swap.maxSize),
301 percent((int) (Config.Swap.maxSize - store_swap_size), (int) Config.Swap.maxSize));
302 /* FIXME Here we should output memory statistics */
303
304 /* Now go through each swapdir, calling its statfs routine */
305 for (i = 0; i < Config.cacheSwap.n_configured; i++) {
306 storeAppendPrintf(sentry, "\n");
307 SD = &(Config.cacheSwap.swapDirs[i]);
308 storeAppendPrintf(sentry, "Store Directory #%d (%s): %s\n", i, SD->type,
309 storeSwapDir(i));
310 storeAppendPrintf(sentry, "FS Block Size %d Bytes\n",
311 SD->fs.blksize);
312 SD->statfs(SD, sentry);
313 if (SD->repl) {
314 storeAppendPrintf(sentry, "Removal policy: %s\n", SD->repl->_type);
315 if (SD->repl->Stats)
316 SD->repl->Stats(SD->repl, sentry);
317 }
318 }
319 }
320
321 void
322 storeDirConfigure(void)
323 {
324 SwapDir *SD;
325 int i;
326 Config.Swap.maxSize = 0;
327 for (i = 0; i < Config.cacheSwap.n_configured; i++) {
328 SD = &Config.cacheSwap.swapDirs[i];
329 Config.Swap.maxSize += SD->max_size;
330 SD->low_size = (int) (((float) SD->max_size *
331 (float) Config.Swap.lowWaterMark) / 100.0);
332 }
333 }
334
335 void
336 storeDirDiskFull(sdirno dirn)
337 {
338 SwapDir *SD = &Config.cacheSwap.swapDirs[dirn];
339 assert(0 <= dirn && dirn < Config.cacheSwap.n_configured);
340 if (SD->cur_size >= SD->max_size)
341 return;
342 SD->max_size = SD->cur_size;
343 debug(20, 1) ("WARNING: Shrinking cache_dir #%d to %d KB\n",
344 dirn, SD->cur_size);
345 }
346
347 void
348 storeDirOpenSwapLogs(void)
349 {
350 int dirn;
351 SwapDir *sd;
352 for (dirn = 0; dirn < Config.cacheSwap.n_configured; dirn++) {
353 sd = &Config.cacheSwap.swapDirs[dirn];
354 if (sd->log.open)
355 sd->log.open(sd);
356 }
357 }
358
359 void
360 storeDirCloseSwapLogs(void)
361 {
362 int dirn;
363 SwapDir *sd;
364 for (dirn = 0; dirn < Config.cacheSwap.n_configured; dirn++) {
365 sd = &Config.cacheSwap.swapDirs[dirn];
366 if (sd->log.close)
367 sd->log.close(sd);
368 }
369 }
370
371 /*
372 * storeDirWriteCleanLogs
373 *
374 * Writes a "clean" swap log file from in-memory metadata.
375 * This is a rewrite of the original function to troll each
376 * StoreDir and write the logs, and flush at the end of
377 * the run. Thanks goes to Eric Stern, since this solution
378 * came out of his COSS code.
379 */
380 #define CLEAN_BUF_SZ 16384
381 int
382 storeDirWriteCleanLogs(int reopen)
383 {
384 const StoreEntry *e = NULL;
385 int n = 0;
386 struct timeval start;
387 double dt;
388 SwapDir *sd;
389 int dirn;
390 int notdone = 1;
391 if (store_dirs_rebuilding) {
392 debug(20, 1) ("Not currently OK to rewrite swap log.\n");
393 debug(20, 1) ("storeDirWriteCleanLogs: Operation aborted.\n");
394 return 0;
395 }
396 debug(20, 1) ("storeDirWriteCleanLogs: Starting...\n");
397 getCurrentTime();
398 start = current_time;
399 for (dirn = 0; dirn < Config.cacheSwap.n_configured; dirn++) {
400 sd = &Config.cacheSwap.swapDirs[dirn];
401 if (sd->log.clean.start(sd) < 0) {
402 debug(20, 1) ("log.clean.start() failed for dir #%d\n", sd->index);
403 continue;
404 }
405 }
406 while (notdone) {
407 notdone = 0;
408 for (dirn = 0; dirn < Config.cacheSwap.n_configured; dirn++) {
409 sd = &Config.cacheSwap.swapDirs[dirn];
410 if (NULL == sd->log.clean.write)
411 continue;
412 e = sd->log.clean.nextentry(sd);
413 if (!e)
414 continue;
415 notdone = 1;
416 if (e->swap_filen < 0)
417 continue;
418 if (e->swap_status != SWAPOUT_DONE)
419 continue;
420 if (e->swap_file_sz <= 0)
421 continue;
422 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
423 continue;
424 if (EBIT_TEST(e->flags, KEY_PRIVATE))
425 continue;
426 if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
427 continue;
428 sd->log.clean.write(sd, e);
429 if ((++n & 0xFFFF) == 0) {
430 getCurrentTime();
431 debug(20, 1) (" %7d entries written so far.\n", n);
432 }
433 }
434 }
435 /* Flush */
436 for (dirn = 0; dirn < Config.cacheSwap.n_configured; dirn++) {
437 sd = &Config.cacheSwap.swapDirs[dirn];
438 sd->log.clean.done(sd);
439 }
440 if (reopen)
441 storeDirOpenSwapLogs();
442 getCurrentTime();
443 dt = tvSubDsec(start, current_time);
444 debug(20, 1) (" Finished. Wrote %d entries.\n", n);
445 debug(20, 1) (" Took %3.1f seconds (%6.1f entries/sec).\n",
446 dt, (double) n / (dt > 0.0 ? dt : 1.0));
447 return n;
448 }
449 #undef CLEAN_BUF_SZ
450
451 /*
452 * sync all avaliable fs'es ..
453 */
454 void
455 storeDirSync(void)
456 {
457 int i;
458 SwapDir *SD;
459
460 for (i = 0; i < Config.cacheSwap.n_configured; i++) {
461 SD = &Config.cacheSwap.swapDirs[i];
462 if (SD->sync != NULL)
463 SD->sync(SD);
464 }
465 }
466
467 /*
468 * handle callbacks all avaliable fs'es ..
469 */
470 void
471 storeDirCallback(void)
472 {
473 int i, j;
474 SwapDir *SD;
475 static int ndir = 0;
476 do {
477 j = 0;
478 for (i = 0; i < Config.cacheSwap.n_configured; i++) {
479 if (ndir >= Config.cacheSwap.n_configured)
480 ndir = ndir % Config.cacheSwap.n_configured;
481 SD = &Config.cacheSwap.swapDirs[ndir++];
482 if (NULL == SD->callback)
483 continue;
484 j += SD->callback(SD);
485 }
486 } while (j > 0);
487 ndir++;
488 }
489
490 int
491 storeDirGetBlkSize(const char *path, int *blksize)
492 {
493 #if HAVE_STATVFS
494 struct statvfs sfs;
495 if (statvfs(path, &sfs)) {
496 debug(50, 1) ("%s: %s\n", path, xstrerror());
497 *blksize = 2048;
498 return 1;
499 }
500 *blksize = (int) sfs.f_frsize;
501 #else
502 struct statfs sfs;
503 if (statfs(path, &sfs)) {
504 debug(50, 1) ("%s: %s\n", path, xstrerror());
505 *blksize = 2048;
506 return 1;
507 }
508 *blksize = (int) sfs.f_bsize;
509 #endif
510 /*
511 * Sanity check; make sure we have a meaningful value.
512 */
513 if (*blksize < 512)
514 *blksize = 2048;
515 return 0;
516 }
517
518 #define fsbtoblk(num, fsbs, bs) \
519 (((fsbs) != 0 && (fsbs) < (bs)) ? \
520 (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
521 int
522 storeDirGetUFSStats(const char *path, int *totl_kb, int *free_kb, int *totl_in, int *free_in)
523 {
524 #if HAVE_STATVFS
525 struct statvfs sfs;
526 if (statvfs(path, &sfs)) {
527 debug(50, 1) ("%s: %s\n", path, xstrerror());
528 return 1;
529 }
530 *totl_kb = (int) fsbtoblk(sfs.f_blocks, sfs.f_frsize, 1024);
531 *free_kb = (int) fsbtoblk(sfs.f_bfree, sfs.f_frsize, 1024);
532 *totl_in = (int) sfs.f_files;
533 *free_in = (int) sfs.f_ffree;
534 #else
535 struct statfs sfs;
536 if (statfs(path, &sfs)) {
537 debug(50, 1) ("%s: %s\n", path, xstrerror());
538 return 1;
539 }
540 *totl_kb = (int) fsbtoblk(sfs.f_blocks, sfs.f_bsize, 1024);
541 *free_kb = (int) fsbtoblk(sfs.f_bfree, sfs.f_bsize, 1024);
542 *totl_in = (int) sfs.f_files;
543 *free_in = (int) sfs.f_ffree;
544 #endif
545 return 0;
546 }