3 * $Id: store_dir.cc,v 1.118 2001/01/02 01:41:31 wessels Exp $
5 * DEBUG: section 47 Store Directory Routines
6 * AUTHOR: Duane Wessels
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * the Regents of the University of California. Please see the
16 * COPYRIGHT file for full details. Squid incorporates software
17 * developed and/or copyrighted by other sources. Please see the
18 * 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 static int storeDirValidSwapDirSize(int, ssize_t
);
39 static STDIRSELECT storeDirSelectSwapDirRoundRobin
;
40 static STDIRSELECT storeDirSelectSwapDirLeastLoad
;
43 * This function pointer is set according to 'store_dir_select_algorithm'
46 STDIRSELECT
*storeDirSelectSwapDir
= storeDirSelectSwapDirLeastLoad
;
53 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
54 sd
= &Config
.cacheSwap
.swapDirs
[i
];
57 if (0 == strcasecmp(Config
.store_dir_select_algorithm
, "round-robin")) {
58 storeDirSelectSwapDir
= storeDirSelectSwapDirRoundRobin
;
59 debug(47, 1) ("Using Round Robin store dir selection\n");
61 storeDirSelectSwapDir
= storeDirSelectSwapDirLeastLoad
;
62 debug(47, 1) ("Using Least Load store dir selection\n");
67 storeCreateSwapDirectories(void)
73 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
76 sd
= &Config
.cacheSwap
.swapDirs
[i
];
82 pid
= wait3(&status
, WNOHANG
, NULL
);
84 pid
= waitpid(-1, &status
, 0);
86 } while (pid
> 0 || (pid
< 0 && errno
== EINTR
));
90 * Determine whether the given directory can handle this object
93 * Note: if the object size is -1, then the only swapdirs that
94 * will return true here are ones that have max_obj_size = -1,
95 * ie any-sized-object swapdirs. This is a good thing.
98 storeDirValidSwapDirSize(int swapdir
, ssize_t objsize
)
101 * If the swapdir's max_obj_size is -1, then it definitely can
103 if (Config
.cacheSwap
.swapDirs
[swapdir
].max_objsize
== -1)
106 * Else, make sure that the max object size is larger than objsize
108 if (Config
.cacheSwap
.swapDirs
[swapdir
].max_objsize
> objsize
)
116 * This new selection scheme simply does round-robin on all SwapDirs.
117 * A SwapDir is skipped if it is over the max_size (100%) limit. If
118 * all SwapDir's are above the limit, then the first dirn that we
119 * checked is returned. Note that 'dirn' is guaranteed to advance even
120 * if all SwapDirs are full.
122 * XXX This function does NOT account for the read_only flag!
125 storeDirSelectSwapDirRoundRobin(const StoreEntry
* unused
)
131 * yes, the '<=' is intentional. If all dirs are full we want to
132 * make sure 'dirn' advances every time this gets called, otherwise
133 * we get stuck on one dir.
135 for (i
= 0; i
<= Config
.cacheSwap
.n_configured
; i
++) {
136 if (++dirn
>= Config
.cacheSwap
.n_configured
)
138 sd
= &Config
.cacheSwap
.swapDirs
[dirn
];
139 if (sd
->cur_size
> sd
->max_size
)
147 * Spread load across all of the store directories
149 * Note: We should modify this later on to prefer sticking objects
150 * in the *tightest fit* swapdir to conserve space, along with the
151 * actual swapdir usage. But for now, this hack will do while
152 * testing, so you should order your swapdirs in the config file
153 * from smallest maxobjsize to unlimited (-1) maxobjsize.
155 * We also have to choose nleast == nconf since we need to consider
156 * ALL swapdirs, regardless of state. Again, this is a hack while
157 * we sort out the real usefulness of this algorithm.
160 storeDirSelectSwapDirLeastLoad(const StoreEntry
* e
)
164 ssize_t least_objsize
;
165 int least_load
= 1000;
171 /* Calculate the object size */
172 objsize
= (ssize_t
) objectLen(e
);
174 objsize
+= e
->mem_obj
->swap_hdr_sz
;
175 /* Initial defaults */
176 least_size
= Config
.cacheSwap
.swapDirs
[0].cur_size
;
177 least_objsize
= Config
.cacheSwap
.swapDirs
[0].max_objsize
;
178 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
179 SD
= &Config
.cacheSwap
.swapDirs
[i
];
180 SD
->flags
.selected
= 0;
181 if (SD
->flags
.read_only
)
183 /* Valid for object size check */
184 if (!storeDirValidSwapDirSize(i
, objsize
))
186 load
= SD
->checkobj(SD
, e
);
189 if (SD
->cur_size
> SD
->max_size
)
191 if (load
> least_load
)
193 if ((least_objsize
> 0) && (objsize
> least_objsize
))
195 /* Only use leastsize if the load is equal */
196 if ((load
== least_load
) && (SD
->cur_size
> least_size
))
199 least_size
= SD
->cur_size
;
204 Config
.cacheSwap
.swapDirs
[dirn
].flags
.selected
= 1;
212 storeSwapDir(int dirn
)
214 assert(0 <= dirn
&& dirn
< Config
.cacheSwap
.n_configured
);
215 return Config
.cacheSwap
.swapDirs
[dirn
].path
;
219 * An entry written to the swap log MUST have the following
221 * 1. It MUST be a public key. It does no good to log
222 * a public ADD, change the key, then log a private
223 * DEL. So we need to log a DEL before we change a
224 * key from public to private.
225 * 2. It MUST have a valid (> -1) swap_filen.
228 storeDirSwapLog(const StoreEntry
* e
, int op
)
231 assert(!EBIT_TEST(e
->flags
, KEY_PRIVATE
));
232 assert(e
->swap_filen
>= 0);
234 * icons and such; don't write them to the swap log
236 if (EBIT_TEST(e
->flags
, ENTRY_SPECIAL
))
238 assert(op
> SWAP_LOG_NOP
&& op
< SWAP_LOG_MAX
);
239 debug(20, 3) ("storeDirSwapLog: %s %s %d %08X\n",
241 storeKeyText(e
->hash
.key
),
244 sd
= &Config
.cacheSwap
.swapDirs
[e
->swap_dirn
];
245 sd
->log
.write(sd
, e
, op
);
249 storeDirUpdateSwapSize(SwapDir
* SD
, size_t size
, int sign
)
251 int blks
= (size
+ SD
->fs
.blksize
- 1) / SD
->fs
.blksize
;
252 int k
= blks
* SD
->fs
.kperblk
* sign
;
254 store_swap_size
+= k
;
262 storeDirStats(StoreEntry
* sentry
)
267 storeAppendPrintf(sentry
, "Store Directory Statistics:\n");
268 storeAppendPrintf(sentry
, "Store Entries : %d\n",
269 memInUse(MEM_STOREENTRY
));
270 storeAppendPrintf(sentry
, "Maximum Swap Size : %8d KB\n",
271 Config
.Swap
.maxSize
);
272 storeAppendPrintf(sentry
, "Current Store Swap Size: %8d KB\n",
274 storeAppendPrintf(sentry
, "Current Capacity : %d%% used, %d%% free\n",
275 percent((int) store_swap_size
, (int) Config
.Swap
.maxSize
),
276 percent((int) (Config
.Swap
.maxSize
- store_swap_size
), (int) Config
.Swap
.maxSize
));
278 /* Now go through each swapdir, calling its statfs routine */
279 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
280 storeAppendPrintf(sentry
, "\n");
281 SD
= &(Config
.cacheSwap
.swapDirs
[i
]);
282 storeAppendPrintf(sentry
, "Store Directory #%d (%s): %s\n", i
, SD
->type
,
284 storeAppendPrintf(sentry
, "FS Block Size %d KB\n",
286 SD
->statfs(SD
, sentry
);
291 storeDirConfigure(void)
295 Config
.Swap
.maxSize
= 0;
296 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
297 SD
= &Config
.cacheSwap
.swapDirs
[i
];
298 Config
.Swap
.maxSize
+= SD
->max_size
;
299 SD
->low_size
= (int) (((float) SD
->max_size
*
300 (float) Config
.Swap
.lowWaterMark
) / 100.0);
305 storeDirDiskFull(sdirno dirn
)
307 SwapDir
*SD
= &Config
.cacheSwap
.swapDirs
[dirn
];
308 assert(0 <= dirn
&& dirn
< Config
.cacheSwap
.n_configured
);
309 if (SD
->cur_size
>= SD
->max_size
)
311 SD
->max_size
= SD
->cur_size
;
312 debug(20, 1) ("WARNING: Shrinking cache_dir #%d to %d KB\n",
317 storeDirOpenSwapLogs(void)
321 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++) {
322 sd
= &Config
.cacheSwap
.swapDirs
[dirn
];
328 storeDirCloseSwapLogs(void)
332 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++) {
333 sd
= &Config
.cacheSwap
.swapDirs
[dirn
];
339 * storeDirWriteCleanLogs
341 * Writes a "clean" swap log file from in-memory metadata.
342 * This is a rewrite of the original function to troll each
343 * StoreDir and write the logs, and flush at the end of
344 * the run. Thanks goes to Eric Stern, since this solution
345 * came out of his COSS code.
347 #define CLEAN_BUF_SZ 16384
349 storeDirWriteCleanLogs(int reopen
)
351 const StoreEntry
*e
= NULL
;
353 struct timeval start
;
356 RemovalPolicyWalker
**walkers
;
359 if (store_dirs_rebuilding
) {
360 debug(20, 1) ("Not currently OK to rewrite swap log.\n");
361 debug(20, 1) ("storeDirWriteCleanLogs: Operation aborted.\n");
364 debug(20, 1) ("storeDirWriteCleanLogs: Starting...\n");
366 start
= current_time
;
367 walkers
= xcalloc(Config
.cacheSwap
.n_configured
, sizeof *walkers
);
368 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++) {
369 sd
= &Config
.cacheSwap
.swapDirs
[dirn
];
370 if (sd
->log
.clean
.start(sd
) < 0) {
371 debug(20, 1) ("log.clean.start() failed for dir #%d\n", sd
->index
);
377 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++) {
378 sd
= &Config
.cacheSwap
.swapDirs
[dirn
];
379 if (NULL
== sd
->log
.clean
.write
)
381 e
= sd
->log
.clean
.nextentry(sd
);
385 if (e
->swap_filen
< 0)
387 if (e
->swap_status
!= SWAPOUT_DONE
)
389 if (e
->swap_file_sz
<= 0)
391 if (EBIT_TEST(e
->flags
, RELEASE_REQUEST
))
393 if (EBIT_TEST(e
->flags
, KEY_PRIVATE
))
395 if (EBIT_TEST(e
->flags
, ENTRY_SPECIAL
))
397 sd
->log
.clean
.write(sd
, e
);
398 if ((++n
& 0xFFFF) == 0) {
400 debug(20, 1) (" %7d entries written so far.\n", n
);
405 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++) {
406 sd
= &Config
.cacheSwap
.swapDirs
[dirn
];
407 sd
->log
.clean
.done(sd
);
410 storeDirOpenSwapLogs();
412 dt
= tvSubDsec(start
, current_time
);
413 debug(20, 1) (" Finished. Wrote %d entries.\n", n
);
414 debug(20, 1) (" Took %3.1f seconds (%6.1f entries/sec).\n",
415 dt
, (double) n
/ (dt
> 0.0 ? dt
: 1.0));
421 * sync all avaliable fs'es ..
429 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
430 SD
= &Config
.cacheSwap
.swapDirs
[i
];
431 if (SD
->sync
!= NULL
)
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
;
450 SD
= &Config
.cacheSwap
.swapDirs
[ndir
++];
451 if (NULL
== SD
->callback
)
453 j
+= SD
->callback(SD
);
460 storeDirGetBlkSize(const char *path
, int *blksize
)
464 if (statvfs(path
, &sfs
)) {
465 debug(0, 0) ("%s: %s\n", path
, xstrerror());
470 if (statfs(path
, &sfs
)) {
471 debug(0, 0) ("%s: %s\n", path
, xstrerror());
475 *blksize
= (int) sfs
.f_bsize
;