3 * $Id: store_dir.cc,v 1.141 2003/01/23 00:37:26 robertc Exp $
5 * DEBUG: section 47 Store Directory Routines
6 * AUTHOR: Duane Wessels
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
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.
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.
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.
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.
38 #include "MemObject.h"
42 #if HAVE_SYS_STATVFS_H
43 #include <sys/statvfs.h>
46 /* Windows uses sys/vfs.h */
51 static int storeDirValidSwapDirSize(int, ssize_t
);
52 static STDIRSELECT storeDirSelectSwapDirRoundRobin
;
53 static STDIRSELECT storeDirSelectSwapDirLeastLoad
;
56 * This function pointer is set according to 'store_dir_select_algorithm'
59 STDIRSELECT
*storeDirSelectSwapDir
= storeDirSelectSwapDirLeastLoad
;
65 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++)
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");
71 storeDirSelectSwapDir
= storeDirSelectSwapDirLeastLoad
;
72 debug(47, 1) ("Using Least Load store dir selection\n");
77 storeCreateSwapDirectories(void)
82 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
85 INDEXSD(i
)->newFileSystem();
90 pid
= wait3(&status
, WNOHANG
, NULL
);
92 pid
= waitpid(-1, &status
, 0);
94 } while (pid
> 0 || (pid
< 0 && errno
== EINTR
));
98 * Determine whether the given directory can handle this object
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.
106 storeDirValidSwapDirSize(int swapdir
, ssize_t objsize
)
109 * If the swapdir's max_obj_size is -1, then it definitely can
111 if (INDEXSD(swapdir
)->max_objsize
== -1)
115 * If the object size is -1, then if the storedir isn't -1 we
118 if ((objsize
== -1) &&
119 (INDEXSD(swapdir
)->max_objsize
!= -1))
123 * Else, make sure that the max object size is larger than objsize
125 if (INDEXSD(swapdir
)->max_objsize
> objsize
)
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
138 storeDirSelectSwapDirRoundRobin(const StoreEntry
* e
)
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
)
149 if (sd
->flags
.read_only
)
151 if (sd
->cur_size
> sd
->max_size
)
153 if (!storeDirValidSwapDirSize(i
, objsize
))
155 /* check for error or overload condition */
156 load
= sd
->canStore(*e
);
157 if (load
< 0 || load
> 1000) {
166 * Spread load across all of the store directories
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.
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.
179 storeDirSelectSwapDirLeastLoad(const StoreEntry
* e
)
182 ssize_t most_free
= 0, cur_free
;
183 ssize_t least_objsize
= -1;
184 int least_load
= INT_MAX
;
190 /* Calculate the object size */
191 objsize
= (ssize_t
) objectLen(e
);
193 objsize
+= e
->mem_obj
->swap_hdr_sz
;
194 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
196 SD
->flags
.selected
= 0;
197 load
= SD
->canStore(*e
);
198 if (load
< 0 || load
> 1000) {
201 if (!storeDirValidSwapDirSize(i
, objsize
))
203 if (SD
->flags
.read_only
)
205 if (SD
->cur_size
> SD
->max_size
)
207 if (load
> least_load
)
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)
217 if (cur_free
< most_free
)
221 least_objsize
= SD
->max_objsize
;
222 most_free
= cur_free
;
226 INDEXSD(dirn
)->flags
.selected
= 1;
233 storeSwapDir(int dirn
)
235 assert(0 <= dirn
&& dirn
< Config
.cacheSwap
.n_configured
);
236 return INDEXSD(dirn
)->path
;
240 * An entry written to the swap log MUST have the following
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.
249 storeDirSwapLog(const StoreEntry
* e
, int op
)
252 assert(!EBIT_TEST(e
->flags
, KEY_PRIVATE
));
253 assert(e
->swap_filen
>= 0);
255 * icons and such; don't write them to the swap log
257 if (EBIT_TEST(e
->flags
, ENTRY_SPECIAL
))
259 assert(op
> SWAP_LOG_NOP
&& op
< SWAP_LOG_MAX
);
260 debug(20, 3) ("storeDirSwapLog: %s %s %d %08X\n",
265 INDEXSD(e
->swap_dirn
)->logEntry(*e
, op
);
269 storeDirUpdateSwapSize(SwapDir
* SD
, size_t size
, int sign
)
271 int blks
= (size
+ SD
->fs
.blksize
- 1) / SD
->fs
.blksize
;
272 int k
= (blks
* SD
->fs
.blksize
>> 10) * sign
;
274 store_swap_size
+= k
;
282 storeDirStats(StoreEntry
* sentry
)
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",
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 */
300 /* Now go through each swapdir, calling its statfs routine */
301 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
302 storeAppendPrintf(sentry
, "\n");
304 storeAppendPrintf(sentry
, "Store Directory #%d (%s): %s\n", i
, SD
->type
,
306 storeAppendPrintf(sentry
, "FS Block Size %d Bytes\n",
310 storeAppendPrintf(sentry
, "Removal policy: %s\n", SD
->repl
->_type
);
312 SD
->repl
->Stats(SD
->repl
, sentry
);
318 storeDirConfigure(void)
322 Config
.Swap
.maxSize
= 0;
323 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
325 Config
.Swap
.maxSize
+= SD
->max_size
;
326 SD
->low_size
= (int) (((float) SD
->max_size
*
327 (float) Config
.Swap
.lowWaterMark
) / 100.0);
332 storeDirDiskFull(sdirno dirn
)
334 SwapDir
*SD
= INDEXSD(dirn
);
335 assert(0 <= dirn
&& dirn
< Config
.cacheSwap
.n_configured
);
336 if (SD
->cur_size
>= SD
->max_size
)
338 SD
->max_size
= SD
->cur_size
;
339 debug(20, 1) ("WARNING: Shrinking cache_dir #%d to %d KB\n",
344 storeDirOpenSwapLogs(void)
346 for (int dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; ++dirn
)
347 INDEXSD(dirn
)->openLog();
351 storeDirCloseSwapLogs(void)
353 for (int dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; ++dirn
)
354 INDEXSD(dirn
)->closeLog();
358 * storeDirWriteCleanLogs
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.
367 storeDirWriteCleanLogs(int reopen
)
369 const StoreEntry
*e
= NULL
;
371 struct timeval start
;
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");
381 debug(20, 1) ("storeDirWriteCleanLogs: Starting...\n");
383 start
= current_time
;
384 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++) {
386 if (sd
->writeCleanStart() < 0) {
387 debug(20, 1) ("log.clean.start() failed for dir #%d\n", sd
->index
);
391 /* This writes all logs in parallel. It seems to me to be more efficient
392 * to write them sequentially. - RBC 20021214
396 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++) {
398 if (NULL
== sd
->cleanLog
)
400 e
= sd
->cleanLog
->nextEntry();
406 sd
->cleanLog
->write(*e
);
407 if ((++n
& 0xFFFF) == 0) {
409 debug(20, 1) (" %7d entries written so far.\n", n
);
414 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++)
415 INDEXSD(dirn
)->writeCleanDone();
417 storeDirOpenSwapLogs();
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));
427 * sync all avaliable fs'es ..
432 for (int i
= 0; i
< Config
.cacheSwap
.n_configured
; ++i
)
437 * handle callbacks all avaliable fs'es ..
440 storeDirCallback(void)
447 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
448 if (ndir
>= Config
.cacheSwap
.n_configured
)
449 ndir
= ndir
% Config
.cacheSwap
.n_configured
;
459 storeDirGetBlkSize(const char *path
, int *blksize
)
463 if (statvfs(path
, &sfs
)) {
464 debug(50, 1) ("%s: %s\n", path
, xstrerror());
468 *blksize
= (int) sfs
.f_frsize
;
471 if (statfs(path
, &sfs
)) {
472 debug(50, 1) ("%s: %s\n", path
, xstrerror());
476 *blksize
= (int) sfs
.f_bsize
;
479 * Sanity check; make sure we have a meaningful value.
486 #define fsbtoblk(num, fsbs, bs) \
487 (((fsbs) != 0 && (fsbs) < (bs)) ? \
488 (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
490 storeDirGetUFSStats(const char *path
, int *totl_kb
, int *free_kb
, int *totl_in
, int *free_in
)
494 if (statvfs(path
, &sfs
)) {
495 debug(50, 1) ("%s: %s\n", path
, xstrerror());
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
;
504 if (statfs(path
, &sfs
)) {
505 debug(50, 1) ("%s: %s\n", path
, xstrerror());
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
;