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