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