3 * $Id: store_dir.cc,v 1.137 2002/10/13 20:35:05 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.
40 #if HAVE_SYS_STATVFS_H
41 #include <sys/statvfs.h>
44 /* Windows uses sys/vfs.h */
49 static int storeDirValidSwapDirSize(int, ssize_t
);
50 static STDIRSELECT storeDirSelectSwapDirRoundRobin
;
51 static STDIRSELECT storeDirSelectSwapDirLeastLoad
;
54 * This function pointer is set according to 'store_dir_select_algorithm'
57 STDIRSELECT
*storeDirSelectSwapDir
= storeDirSelectSwapDirLeastLoad
;
64 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
65 sd
= &Config
.cacheSwap
.swapDirs
[i
];
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");
72 storeDirSelectSwapDir
= storeDirSelectSwapDirLeastLoad
;
73 debug(47, 1) ("Using Least Load store dir selection\n");
78 storeCreateSwapDirectories(void)
84 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
87 sd
= &Config
.cacheSwap
.swapDirs
[i
];
88 if (NULL
!= sd
->newfs
)
94 pid
= wait3(&status
, WNOHANG
, NULL
);
96 pid
= waitpid(-1, &status
, 0);
98 } while (pid
> 0 || (pid
< 0 && errno
== EINTR
));
102 * Determine whether the given directory can handle this object
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.
110 storeDirValidSwapDirSize(int swapdir
, ssize_t objsize
)
113 * If the swapdir's max_obj_size is -1, then it definitely can
115 if (Config
.cacheSwap
.swapDirs
[swapdir
].max_objsize
== -1)
119 * If the object size is -1, then if the storedir isn't -1 we
122 if ((objsize
== -1) &&
123 (Config
.cacheSwap
.swapDirs
[swapdir
].max_objsize
!= -1))
127 * Else, make sure that the max object size is larger than objsize
129 if (Config
.cacheSwap
.swapDirs
[swapdir
].max_objsize
> objsize
)
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
142 storeDirSelectSwapDirRoundRobin(const StoreEntry
* e
)
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
)
152 sd
= &Config
.cacheSwap
.swapDirs
[dirn
];
153 if (sd
->flags
.read_only
)
155 if (sd
->cur_size
> sd
->max_size
)
157 if (!storeDirValidSwapDirSize(i
, objsize
))
159 /* check for error or overload condition */
160 load
= sd
->checkobj(sd
, e
);
161 if (load
< 0 || load
> 1000) {
170 * Spread load across all of the store directories
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.
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.
183 storeDirSelectSwapDirLeastLoad(const StoreEntry
* e
)
186 ssize_t most_free
= 0, cur_free
;
187 ssize_t least_objsize
= -1;
188 int least_load
= INT_MAX
;
194 /* Calculate the object size */
195 objsize
= (ssize_t
) objectLen(e
);
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) {
205 if (!storeDirValidSwapDirSize(i
, objsize
))
207 if (SD
->flags
.read_only
)
209 if (SD
->cur_size
> SD
->max_size
)
211 if (load
> least_load
)
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)
221 if (cur_free
< most_free
)
225 least_objsize
= SD
->max_objsize
;
226 most_free
= cur_free
;
230 Config
.cacheSwap
.swapDirs
[dirn
].flags
.selected
= 1;
237 storeSwapDir(int dirn
)
239 assert(0 <= dirn
&& dirn
< Config
.cacheSwap
.n_configured
);
240 return Config
.cacheSwap
.swapDirs
[dirn
].path
;
244 * An entry written to the swap log MUST have the following
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.
253 storeDirSwapLog(const StoreEntry
* e
, int op
)
256 assert(!EBIT_TEST(e
->flags
, KEY_PRIVATE
));
257 assert(e
->swap_filen
>= 0);
259 * icons and such; don't write them to the swap log
261 if (EBIT_TEST(e
->flags
, ENTRY_SPECIAL
))
263 assert(op
> SWAP_LOG_NOP
&& op
< SWAP_LOG_MAX
);
264 debug(20, 3) ("storeDirSwapLog: %s %s %d %08X\n",
266 storeKeyText((const cache_key
*)e
->hash
.key
),
269 sd
= &Config
.cacheSwap
.swapDirs
[e
->swap_dirn
];
270 sd
->log
.write(sd
, e
, op
);
274 storeDirUpdateSwapSize(SwapDir
* SD
, size_t size
, int sign
)
276 int blks
= (size
+ SD
->fs
.blksize
- 1) / SD
->fs
.blksize
;
277 int k
= (blks
* SD
->fs
.blksize
>> 10) * sign
;
279 store_swap_size
+= k
;
287 storeDirStats(StoreEntry
* sentry
)
292 storeAppendPrintf(sentry
, "Store Directory Statistics:\n");
293 storeAppendPrintf(sentry
, "Store Entries : %d\n",
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",
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 */
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
,
310 storeAppendPrintf(sentry
, "FS Block Size %d Bytes\n",
312 SD
->statfs(SD
, sentry
);
314 storeAppendPrintf(sentry
, "Removal policy: %s\n", SD
->repl
->_type
);
316 SD
->repl
->Stats(SD
->repl
, sentry
);
322 storeDirConfigure(void)
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);
336 storeDirDiskFull(sdirno dirn
)
338 SwapDir
*SD
= &Config
.cacheSwap
.swapDirs
[dirn
];
339 assert(0 <= dirn
&& dirn
< Config
.cacheSwap
.n_configured
);
340 if (SD
->cur_size
>= SD
->max_size
)
342 SD
->max_size
= SD
->cur_size
;
343 debug(20, 1) ("WARNING: Shrinking cache_dir #%d to %d KB\n",
348 storeDirOpenSwapLogs(void)
352 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++) {
353 sd
= &Config
.cacheSwap
.swapDirs
[dirn
];
360 storeDirCloseSwapLogs(void)
364 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++) {
365 sd
= &Config
.cacheSwap
.swapDirs
[dirn
];
372 * storeDirWriteCleanLogs
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.
380 #define CLEAN_BUF_SZ 16384
382 storeDirWriteCleanLogs(int reopen
)
384 const StoreEntry
*e
= NULL
;
386 struct timeval start
;
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");
396 debug(20, 1) ("storeDirWriteCleanLogs: Starting...\n");
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
);
408 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++) {
409 sd
= &Config
.cacheSwap
.swapDirs
[dirn
];
410 if (NULL
== sd
->log
.clean
.write
)
412 e
= sd
->log
.clean
.nextentry(sd
);
416 if (e
->swap_filen
< 0)
418 if (e
->swap_status
!= SWAPOUT_DONE
)
420 if (e
->swap_file_sz
<= 0)
422 if (EBIT_TEST(e
->flags
, RELEASE_REQUEST
))
424 if (EBIT_TEST(e
->flags
, KEY_PRIVATE
))
426 if (EBIT_TEST(e
->flags
, ENTRY_SPECIAL
))
428 sd
->log
.clean
.write(sd
, e
);
429 if ((++n
& 0xFFFF) == 0) {
431 debug(20, 1) (" %7d entries written so far.\n", n
);
436 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++) {
437 sd
= &Config
.cacheSwap
.swapDirs
[dirn
];
438 sd
->log
.clean
.done(sd
);
441 storeDirOpenSwapLogs();
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));
452 * sync all avaliable fs'es ..
460 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
461 SD
= &Config
.cacheSwap
.swapDirs
[i
];
462 if (SD
->sync
!= NULL
)
468 * handle callbacks all avaliable fs'es ..
471 storeDirCallback(void)
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
)
484 j
+= SD
->callback(SD
);
491 storeDirGetBlkSize(const char *path
, int *blksize
)
495 if (statvfs(path
, &sfs
)) {
496 debug(50, 1) ("%s: %s\n", path
, xstrerror());
500 *blksize
= (int) sfs
.f_frsize
;
503 if (statfs(path
, &sfs
)) {
504 debug(50, 1) ("%s: %s\n", path
, xstrerror());
508 *blksize
= (int) sfs
.f_bsize
;
511 * Sanity check; make sure we have a meaningful value.
518 #define fsbtoblk(num, fsbs, bs) \
519 (((fsbs) != 0 && (fsbs) < (bs)) ? \
520 (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
522 storeDirGetUFSStats(const char *path
, int *totl_kb
, int *free_kb
, int *totl_in
, int *free_in
)
526 if (statvfs(path
, &sfs
)) {
527 debug(50, 1) ("%s: %s\n", path
, xstrerror());
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
;
536 if (statfs(path
, &sfs
)) {
537 debug(50, 1) ("%s: %s\n", path
, xstrerror());
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
;