3 * $Id: store_dir_diskd.cc,v 1.1 2000/05/03 17:15:47 adrian 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 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * 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 #if HAVE_SYS_STATVFS_H
39 #include <sys/statvfs.h>
47 #include "store_diskd.h"
49 #define DefaultLevelOneDirs 16
50 #define DefaultLevelTwoDirs 256
51 #define STORE_META_BDISKDZ 4096
54 #error "SQUID_PREFIX needs defining!"
57 diskd_stats_t diskd_stats
;
59 typedef struct _RebuildState RebuildState
;
60 struct _RebuildState
{
68 unsigned int need_to_validate
:1;
77 char fullpath
[SQUID_MAXPATHLEN
];
78 char fullfilename
[SQUID_MAXPATHLEN
];
79 struct _store_rebuild_data counts
;
82 static int n_diskd_dirs
= 0;
83 static int *diskd_dir_index
= NULL
;
84 MemPool
* diskd_state_pool
= NULL
;
85 static int diskd_initialised
= 0;
87 static char *storeDiskdDirSwapSubDir(SwapDir
*, int subdirn
);
88 static int storeDiskdDirCreateDirectory(const char *path
, int);
89 static int storeDiskdDirVerifyCacheDirs(SwapDir
*);
90 static int storeDiskdDirVerifyDirectory(const char *path
);
91 static void storeDiskdDirCreateSwapSubDirs(SwapDir
*);
92 static char *storeDiskdDirSwapLogFile(SwapDir
*, const char *);
93 static EVH storeDiskdDirRebuildFromDirectory
;
94 static EVH storeDiskdDirRebuildFromSwapLog
;
95 static int storeDiskdDirGetNextFile(RebuildState
*, int *sfileno
, int *size
);
96 static StoreEntry
*storeDiskdDirAddDiskRestore(SwapDir
*SD
, const cache_key
* key
,
106 static void storeDiskdDirRebuild(SwapDir
* sd
);
107 static void storeDiskdDirCloseTmpSwapLog(SwapDir
* sd
);
108 static FILE *storeDiskdDirOpenTmpSwapLog(SwapDir
*, int *, int *);
109 static STLOGOPEN storeDiskdDirOpenSwapLog
;
110 static STINIT storeDiskdDirInit
;
111 static STFREE storeDiskdDirFree
;
112 static STLOGCLEANOPEN storeDiskdDirWriteCleanOpen
;
113 static void storeDiskdDirWriteCleanClose(SwapDir
* sd
);
114 static STLOGCLEANWRITE storeDiskdDirWriteCleanEntry
;
115 static STLOGCLOSE storeDiskdDirCloseSwapLog
;
116 static STLOGWRITE storeDiskdDirSwapLog
;
117 static STNEWFS storeDiskdDirNewfs
;
118 static STDUMP storeDiskdDirDump
;
119 static STMAINTAINFS storeDiskdDirMaintain
;
120 static STCHECKOBJ storeDiskdDirCheckObj
;
121 static STREFOBJ storeDiskdDirRefObj
;
122 static STUNREFOBJ storeDiskdDirUnrefObj
;
123 static QS rev_int_sort
;
124 static int storeDiskdDirClean(int swap_index
);
125 static EVH storeDiskdDirCleanEvent
;
126 static int storeDiskdDirIs(SwapDir
* sd
);
127 static int storeDiskdFilenoBelongsHere(int fn
, int F0
, int F1
, int F2
);
128 static int storeDiskdCleanupDoubleCheck(SwapDir
*, StoreEntry
*);
129 static void storeDiskdDirStats(SwapDir
*, StoreEntry
*);
130 static void storeDiskdDirInitBitmap(SwapDir
*);
131 static int storeDiskdDirValidFileno(SwapDir
*, sfileno
);
132 static int storeDiskdDirCheckExpired(SwapDir
*, StoreEntry
*);
133 #if !HEAP_REPLACEMENT
134 static time_t storeDiskdDirExpiredReferenceAge(SwapDir
*);
136 static void storeDiskdStats(StoreEntry
* sentry
);
137 static void storeDiskdDirSync(SwapDir
*);
138 static void storeDiskdDirCallback(SwapDir
*);
142 * These functions were ripped straight out of the heart of store_dir.c.
143 * They assume that the given filenum is on a diskd partiton, which may or
145 * XXX this evilness should be tidied up at a later date!
149 storeDiskdDirMapBitTest(SwapDir
*SD
, int fn
)
152 diskdinfo_t
*diskdinfo
;
153 diskdinfo
= (diskdinfo_t
*)SD
->fsdata
;
154 return file_map_bit_test(diskdinfo
->map
, filn
);
158 storeDiskdDirMapBitSet(SwapDir
*SD
, int fn
)
161 diskdinfo_t
*diskdinfo
;
162 diskdinfo
= (diskdinfo_t
*)SD
->fsdata
;
163 file_map_bit_set(diskdinfo
->map
, filn
);
167 storeDiskdDirMapBitReset(SwapDir
*SD
, int fn
)
170 diskdinfo_t
*diskdinfo
;
171 diskdinfo
= (diskdinfo_t
*)SD
->fsdata
;
172 file_map_bit_reset(diskdinfo
->map
, filn
);
176 storeDiskdDirMapBitAllocate(SwapDir
*SD
)
178 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)SD
->fsdata
;
180 fn
= file_map_allocate(diskdinfo
->map
, diskdinfo
->suggest
);
181 file_map_bit_set(diskdinfo
->map
, fn
);
182 diskdinfo
->suggest
= fn
+ 1;
187 * Initialise the diskd bitmap
189 * If there already is a bitmap, and the numobjects is larger than currently
190 * configured, we allocate a new bitmap and 'grow' the old one into it.
193 storeDiskdDirInitBitmap(SwapDir
*sd
)
195 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)sd
->fsdata
;
197 if (diskdinfo
->map
== NULL
) {
199 diskdinfo
->map
= file_map_create();
200 } else if (diskdinfo
->map
->max_n_files
) {
201 /* it grew, need to expand */
202 /* XXX We don't need it anymore .. */
204 /* else it shrunk, and we leave the old one in place */
208 storeDiskdDirSwapSubDir(SwapDir
* sd
, int subdirn
)
210 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)sd
->fsdata
;
212 LOCAL_ARRAY(char, fullfilename
, SQUID_MAXPATHLEN
);
213 assert(0 <= subdirn
&& subdirn
< diskdinfo
->l1
);
214 snprintf(fullfilename
, SQUID_MAXPATHLEN
, "%s/%02X", sd
->path
, subdirn
);
219 storeDiskdDirCreateDirectory(const char *path
, int should_exist
)
224 if (0 == stat(path
, &st
)) {
225 if (S_ISDIR(st
.st_mode
)) {
226 debug(20, should_exist
? 3 : 1) ("%s exists\n", path
);
228 fatalf("Swap directory %s is not a directory.", path
);
230 } else if (0 == mkdir(path
, 0755)) {
231 debug(20, should_exist
? 1 : 3) ("%s created\n", path
);
234 fatalf("Failed to make swap directory %s: %s",
241 storeDiskdDirVerifyDirectory(const char *path
)
244 if (stat(path
, &sb
) < 0) {
245 debug(20, 0) ("%s: %s\n", path
, xstrerror());
248 if (S_ISDIR(sb
.st_mode
) == 0) {
249 debug(20, 0) ("%s is not a directory\n", path
);
256 * This function is called by storeDiskdDirInit(). If this returns < 0,
257 * then Squid exits, complains about swap directories not
258 * existing, and instructs the admin to run 'squid -z'
261 storeDiskdDirVerifyCacheDirs(SwapDir
* sd
)
263 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)sd
->fsdata
;
265 const char *path
= sd
->path
;
267 if (storeDiskdDirVerifyDirectory(path
) < 0)
269 for (j
= 0; j
< diskdinfo
->l1
; j
++) {
270 path
= storeDiskdDirSwapSubDir(sd
, j
);
271 if (storeDiskdDirVerifyDirectory(path
) < 0)
278 storeDiskdDirCreateSwapSubDirs(SwapDir
* sd
)
280 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)sd
->fsdata
;
283 LOCAL_ARRAY(char, name
, MAXPATHLEN
);
284 for (i
= 0; i
< diskdinfo
->l1
; i
++) {
285 snprintf(name
, MAXPATHLEN
, "%s/%02X", sd
->path
, i
);
286 if (storeDiskdDirCreateDirectory(name
, 0))
290 debug(47, 1) ("Making directories in %s\n", name
);
291 for (k
= 0; k
< diskdinfo
->l2
; k
++) {
292 snprintf(name
, MAXPATHLEN
, "%s/%02X/%02X", sd
->path
, i
, k
);
293 storeDiskdDirCreateDirectory(name
, should_exist
);
299 storeDiskdDirSwapLogFile(SwapDir
* sd
, const char *ext
)
301 LOCAL_ARRAY(char, path
, SQUID_MAXPATHLEN
);
302 LOCAL_ARRAY(char, pathtmp
, SQUID_MAXPATHLEN
);
303 LOCAL_ARRAY(char, digit
, 32);
305 if (Config
.Log
.swap
) {
306 xstrncpy(pathtmp
, sd
->path
, SQUID_MAXPATHLEN
- 64);
307 while (index(pathtmp
,'/'))
308 *index(pathtmp
,'/')='.';
309 while (strlen(pathtmp
) && pathtmp
[strlen(pathtmp
)-1]=='.')
310 pathtmp
[strlen(pathtmp
)-1]= '\0';
311 for(pathtmp2
= pathtmp
; *pathtmp2
== '.'; pathtmp2
++);
312 snprintf(path
, SQUID_MAXPATHLEN
-64, Config
.Log
.swap
, pathtmp2
);
313 if (strncmp(path
, Config
.Log
.swap
, SQUID_MAXPATHLEN
- 64) == 0) {
315 snprintf(digit
, 32, "%02d", sd
->index
);
316 strncat(path
, digit
, 3);
319 xstrncpy(path
, sd
->path
, SQUID_MAXPATHLEN
- 64);
320 strcat(path
, "/swap.state");
323 strncat(path
, ext
, 16);
328 storeDiskdDirOpenSwapLog(SwapDir
* sd
)
330 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)sd
->fsdata
;
333 path
= storeDiskdDirSwapLogFile(sd
, NULL
);
334 fd
= file_open(path
, O_WRONLY
| O_CREAT
);
336 debug(50, 1) ("%s: %s\n", path
, xstrerror());
337 fatal("storeDiskdDirOpenSwapLog: Failed to open swap log.");
339 debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd
->index
, fd
);
340 diskdinfo
->swaplog_fd
= fd
;
341 if (0 == n_diskd_dirs
)
342 assert(NULL
== diskd_dir_index
);
344 assert(n_diskd_dirs
<= Config
.cacheSwap
.n_configured
);
348 storeDiskdDirCloseSwapLog(SwapDir
* sd
)
350 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)sd
->fsdata
;
351 if (diskdinfo
->swaplog_fd
< 0) /* not open */
353 file_close(diskdinfo
->swaplog_fd
);
354 debug(47, 3) ("Cache Dir #%d log closed on FD %d\n",
355 sd
->index
, diskdinfo
->swaplog_fd
);
356 diskdinfo
->swaplog_fd
= -1;
358 assert(n_diskd_dirs
>= 0);
359 if (0 == n_diskd_dirs
)
360 safe_free(diskd_dir_index
);
364 storeDiskdDirInit(SwapDir
* sd
)
366 static int started_clean_event
= 0;
370 int ikey
= (getpid() << 16) + (sd
->index
<< 4);
375 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*) sd
->fsdata
;
376 static const char *errmsg
=
377 "\tFailed to verify one of the swap directories, Check cache.log\n"
378 "\tfor details. Run 'squid -z' to create swap directories\n"
379 "\tif needed, or if running Squid for the first time.";
381 diskdinfo
->smsgid
= msgget((key_t
) ikey
, 0700 | IPC_CREAT
);
382 if (diskdinfo
->smsgid
< 0) {
383 debug(50, 0) ("storeDiskdInit: msgget: %s\n", xstrerror());
384 fatal("msgget failed");
386 diskdinfo
->rmsgid
= msgget((key_t
) (ikey
+ 1), 0700 | IPC_CREAT
);
387 if (diskdinfo
->rmsgid
< 0) {
388 debug(50, 0) ("storeDiskdInit: msgget: %s\n", xstrerror());
389 fatal("msgget failed");
391 diskdinfo
->shm
.id
= shmget((key_t
) (ikey
+ 2),
392 SHMBUFS
* SHMBUF_BLKSZ
, 0600 | IPC_CREAT
);
393 if (diskdinfo
->shm
.id
< 0) {
394 debug(50, 0) ("storeDiskdInit: shmget: %s\n", xstrerror());
395 fatal("shmget failed");
397 diskdinfo
->shm
.buf
= shmat(diskdinfo
->shm
.id
, NULL
, 0);
398 if (diskdinfo
->shm
.buf
== (void *) -1) {
399 debug(50, 0) ("storeDiskdInit: shmat: %s\n", xstrerror());
400 fatal("shmat failed");
402 diskd_stats
.shmbuf_count
+= SHMBUFS
;
403 for (i
= 0; i
< SHMBUFS
; i
++)
404 storeDiskdShmPut(sd
, i
* SHMBUF_BLKSZ
);
405 snprintf(skey1
, 32, "%d", ikey
);
406 snprintf(skey2
, 32, "%d", ikey
+ 1);
407 snprintf(skey3
, 32, "%d", ikey
+ 2);
413 #if HAVE_POLL && defined(_SQUID_OSF_)
414 /* pipes and poll() don't get along on DUNIX -DW */
415 x
= ipcCreate(IPC_TCP_SOCKET
,
417 x
= ipcCreate(IPC_FIFO
,
419 SQUID_PREFIX
"/bin/diskd",
425 fatal("execl " SQUID_PREFIX
"/bin/diskd failed");
426 if (rfd
!= diskdinfo
->wfd
)
428 fd_note(diskdinfo
->wfd
, "squid -> diskd");
429 commSetTimeout(diskdinfo
->wfd
, -1, NULL
, NULL
);
430 commSetNonBlocking(diskdinfo
->wfd
);
431 storeDiskdDirInitBitmap(sd
);
432 if (storeDiskdDirVerifyCacheDirs(sd
) < 0)
434 storeDiskdDirOpenSwapLog(sd
);
435 storeDiskdDirRebuild(sd
);
436 if (!started_clean_event
) {
437 eventAdd("storeDirClean", storeDiskdDirCleanEvent
, NULL
, 15.0, 1);
438 started_clean_event
= 1;
444 storeDiskdStats(StoreEntry
* sentry
)
446 storeAppendPrintf(sentry
, "sent_count: %d\n", diskd_stats
.sent_count
);
447 storeAppendPrintf(sentry
, "recv_count: %d\n", diskd_stats
.recv_count
);
448 storeAppendPrintf(sentry
, "max_away: %d\n", diskd_stats
.max_away
);
449 storeAppendPrintf(sentry
, "max_shmuse: %d\n", diskd_stats
.max_shmuse
);
450 storeAppendPrintf(sentry
, "open_fail_queue_len: %d\n", diskd_stats
.open_fail_queue_len
);
451 storeAppendPrintf(sentry
, "block_queue_len: %d\n", diskd_stats
.block_queue_len
);
452 diskd_stats
.max_away
= diskd_stats
.max_shmuse
= 0;
458 * Sync any pending data. We just sit around and read the queue
459 * until the data has finished writing.
462 storeDiskdDirSync(SwapDir
*SD
)
464 /* XXX NOT DONE YET! */
465 #warning "storeDiskdSync() needs to be written"
470 * storeDiskdDirCallback
472 * Handle callbacks. If we have more than magic2 requests away, we block
473 * until the queue is below magic2. Otherwise, we simply return when we
474 * don't get a message.
477 storeDiskdDirCallback(SwapDir
*SD
)
481 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)SD
->fsdata
;
483 if (diskdinfo
->away
>= diskdinfo
->magic2
)
484 diskd_stats
.block_queue_len
++;
486 if (diskd_stats
.sent_count
- diskd_stats
.recv_count
>
487 diskd_stats
.max_away
) {
488 diskd_stats
.max_away
= diskd_stats
.sent_count
- diskd_stats
.recv_count
;
489 diskd_stats
.max_shmuse
= diskd_stats
.shmbuf_count
;
492 /* if we are above magic2, we do not break under any reason */
494 memset(&M
, '\0', sizeof(M
));
495 x
= msgrcv(diskdinfo
->rmsgid
, &M
, msg_snd_rcv_sz
, 0, IPC_NOWAIT
);
497 if (diskdinfo
->away
>= diskdinfo
->magic2
)
501 } else if (x
!= msg_snd_rcv_sz
) {
502 debug(81, 1) ("storeDiskdReadIndividualQueue: msgget returns %d\n",
506 diskd_stats
.recv_count
++;
508 storeDiskdHandle(&M
);
509 if (M
.shm_offset
> -1)
510 storeDiskdShmPut(SD
, M
.shm_offset
);
517 storeDiskdDirRebuildFromDirectory(void *data
)
519 RebuildState
*rb
= data
;
520 SwapDir
*SD
= rb
->sd
;
521 LOCAL_ARRAY(char, hdr_buf
, SM_PAGE_SIZE
);
522 StoreEntry
*e
= NULL
;
524 cache_key key
[MD5_DIGEST_CHARS
];
534 debug(20, 3) ("storeDiskdDirRebuildFromDirectory: DIR #%d\n", rb
->sd
->index
);
535 for (count
= 0; count
< rb
->speed
; count
++) {
537 fd
= storeDiskdDirGetNextFile(rb
, &sfileno
, &size
);
539 debug(20, 1) ("Done scanning %s swaplog (%d entries)\n",
540 rb
->sd
->path
, rb
->n_read
);
541 store_dirs_rebuilding
--;
542 storeDiskdDirCloseTmpSwapLog(rb
->sd
);
543 storeRebuildComplete(&rb
->counts
);
550 /* lets get file stats here */
551 if (fstat(fd
, &sb
) < 0) {
552 debug(20, 1) ("storeDiskdDirRebuildFromDirectory: fstat(FD %d): %s\n",
555 store_open_disk_fd
--;
559 if ((++rb
->counts
.scancount
& 0xFFFF) == 0)
560 debug(20, 3) (" %s %7d files opened so far.\n",
561 rb
->sd
->path
, rb
->counts
.scancount
);
562 debug(20, 9) ("file_in: fd=%d %08X\n", fd
, sfileno
);
563 Counter
.syscalls
.disk
.reads
++;
564 if (read(fd
, hdr_buf
, SM_PAGE_SIZE
) < 0) {
565 debug(20, 1) ("storeDiskdDirRebuildFromDirectory: read(FD %d): %s\n",
568 store_open_disk_fd
--;
573 store_open_disk_fd
--;
580 tlv_list
= storeSwapMetaUnpack(hdr_buf
, &swap_hdr_len
);
581 if (tlv_list
== NULL
) {
582 debug(20, 1) ("storeDiskdDirRebuildFromDirectory: failed to get meta data\n");
583 /* XXX shouldn't this be a call to storeDiskdUnlink ? */
584 storeDiskdDirUnlinkFile(SD
, sfileno
);
587 debug(20, 3) ("storeDiskdDirRebuildFromDirectory: successful swap meta unpacking\n");
588 memset(key
, '\0', MD5_DIGEST_CHARS
);
589 memset(&tmpe
, '\0', sizeof(StoreEntry
));
590 for (t
= tlv_list
; t
; t
= t
->next
) {
593 assert(t
->length
== MD5_DIGEST_CHARS
);
594 xmemcpy(key
, t
->value
, MD5_DIGEST_CHARS
);
597 assert(t
->length
== STORE_HDR_METASIZE
);
598 xmemcpy(&tmpe
.timestamp
, t
->value
, STORE_HDR_METASIZE
);
604 storeSwapTLVFree(tlv_list
);
606 if (storeKeyNull(key
)) {
607 debug(20, 1) ("storeDiskdDirRebuildFromDirectory: NULL key\n");
608 storeDiskdDirUnlinkFile(SD
, sfileno
);
613 if (tmpe
.swap_file_sz
== 0) {
614 tmpe
.swap_file_sz
= sb
.st_size
;
615 } else if (tmpe
.swap_file_sz
== sb
.st_size
- swap_hdr_len
) {
616 tmpe
.swap_file_sz
= sb
.st_size
;
617 } else if (tmpe
.swap_file_sz
!= sb
.st_size
) {
618 debug(20, 1) ("storeDiskdDirRebuildFromDirectory: SIZE MISMATCH %d!=%d\n",
619 tmpe
.swap_file_sz
, (int) sb
.st_size
);
620 storeDiskdDirUnlinkFile(SD
, sfileno
);
623 if (EBIT_TEST(tmpe
.flags
, KEY_PRIVATE
)) {
624 storeDiskdDirUnlinkFile(SD
, sfileno
);
625 rb
->counts
.badflags
++;
629 if (e
&& e
->lastref
>= tmpe
.lastref
) {
630 /* key already exists, current entry is newer */
631 /* keep old, ignore new */
632 rb
->counts
.dupcount
++;
634 } else if (NULL
!= e
) {
635 /* URL already exists, this swapfile not being used */
636 /* junk old, load new */
637 storeRelease(e
); /* release old entry */
638 rb
->counts
.dupcount
++;
640 rb
->counts
.objcount
++;
641 storeEntryDump(&tmpe
, 5);
642 e
= storeDiskdDirAddDiskRestore(SD
, key
,
649 tmpe
.refcount
, /* refcount */
650 tmpe
.flags
, /* flags */
651 (int) rb
->flags
.clean
);
652 storeDirSwapLog(e
, SWAP_LOG_ADD
);
654 eventAdd("storeRebuild", storeDiskdDirRebuildFromDirectory
, rb
, 0.0, 1);
658 storeDiskdDirRebuildFromSwapLog(void *data
)
660 RebuildState
*rb
= data
;
661 SwapDir
*SD
= rb
->sd
;
662 StoreEntry
*e
= NULL
;
664 size_t ss
= sizeof(storeSwapLogData
);
666 int used
; /* is swapfile already in use? */
667 int disk_entry_newer
; /* is the log entry newer than current entry? */
670 /* load a number of objects per invocation */
671 for (count
= 0; count
< rb
->speed
; count
++) {
672 if (fread(&s
, ss
, 1, rb
->log
) != 1) {
673 debug(20, 1) ("Done reading %s swaplog (%d entries)\n",
674 rb
->sd
->path
, rb
->n_read
);
677 store_dirs_rebuilding
--;
678 storeDiskdDirCloseTmpSwapLog(rb
->sd
);
679 storeRebuildComplete(&rb
->counts
);
684 if (s
.op
<= SWAP_LOG_NOP
)
686 if (s
.op
>= SWAP_LOG_MAX
)
688 debug(20, 3) ("storeDiskdDirRebuildFromSwapLog: %s %s %08X\n",
689 swap_log_op_str
[(int) s
.op
],
692 if (s
.op
== SWAP_LOG_ADD
) {
694 } else if (s
.op
== SWAP_LOG_DEL
) {
695 if ((e
= storeGet(s
.key
)) != NULL
) {
697 * Make sure we don't unlink the file, it might be
698 * in use by a subsequent entry. Also note that
699 * we don't have to subtract from store_swap_size
700 * because adding to store_swap_size happens in
701 * the cleanup procedure.
704 storeReleaseRequest(e
);
705 storeDiskdDirReplRemove(e
);
706 if (e
->swap_filen
> -1) {
707 storeDiskdDirMapBitReset(SD
, e
->swap_filen
);
712 rb
->counts
.objcount
--;
713 rb
->counts
.cancelcount
++;
717 x
= log(++rb
->counts
.bad_log_op
) / log(10.0);
718 if (0.0 == x
- (double) (int) x
)
719 debug(20, 1) ("WARNING: %d invalid swap log entries found\n",
720 rb
->counts
.bad_log_op
);
721 rb
->counts
.invalid
++;
724 if ((++rb
->counts
.scancount
& 0xFFFF) == 0)
725 debug(20, 3) (" %7d %s Entries read so far.\n",
726 rb
->counts
.scancount
, rb
->sd
->path
);
727 if (!storeDiskdDirValidFileno(SD
, s
.swap_filen
)) {
728 rb
->counts
.invalid
++;
731 if (EBIT_TEST(s
.flags
, KEY_PRIVATE
)) {
732 rb
->counts
.badflags
++;
736 used
= storeDiskdDirMapBitTest(SD
, s
.swap_filen
);
737 /* If this URL already exists in the cache, does the swap log
738 * appear to have a newer entry? Compare 'lastref' from the
739 * swap log to e->lastref. */
740 disk_entry_newer
= e
? (s
.lastref
> e
->lastref
? 1 : 0) : 0;
741 if (used
&& !disk_entry_newer
) {
742 /* log entry is old, ignore it */
743 rb
->counts
.clashcount
++;
745 } else if (used
&& e
&& e
->swap_filen
== s
.swap_filen
&& e
->swap_dirn
== SD
->index
) {
746 /* swapfile taken, same URL, newer, update meta */
747 if (e
->store_status
== STORE_OK
) {
748 e
->lastref
= s
.timestamp
;
749 e
->timestamp
= s
.timestamp
;
750 e
->expires
= s
.expires
;
751 e
->lastmod
= s
.lastmod
;
753 e
->refcount
+= s
.refcount
;
755 storeHeapPositionUpdate(e
, SD
);
756 storeDiskdDirUnrefObj(SD
, e
);
759 debug_trap("storeDiskdDirRebuildFromSwapLog: bad condition");
760 debug(20, 1) ("\tSee %s:%d\n", __FILE__
, __LINE__
);
764 /* swapfile in use, not by this URL, log entry is newer */
765 /* This is sorta bad: the log entry should NOT be newer at this
766 * point. If the log is dirty, the filesize check should have
767 * caught this. If the log is clean, there should never be a
769 debug(20, 1) ("WARNING: newer swaplog entry for dirno %d, fileno %08X\n",
770 SD
->index
, s
.swap_filen
);
771 /* I'm tempted to remove the swapfile here just to be safe,
772 * but there is a bad race condition in the NOVM version if
773 * the swapfile has recently been opened for writing, but
774 * not yet opened for reading. Because we can't map
775 * swapfiles back to StoreEntrys, we don't know the state
776 * of the entry using that file. */
777 /* We'll assume the existing entry is valid, probably because
778 * were in a slow rebuild and the the swap file number got taken
779 * and the validation procedure hasn't run. */
780 assert(rb
->flags
.need_to_validate
);
781 rb
->counts
.clashcount
++;
783 } else if (e
&& !disk_entry_newer
) {
784 /* key already exists, current entry is newer */
785 /* keep old, ignore new */
786 rb
->counts
.dupcount
++;
789 /* key already exists, this swapfile not being used */
790 /* junk old, load new */
792 storeReleaseRequest(e
);
793 storeDiskdDirReplRemove(e
);
794 if (e
->swap_filen
> -1) {
795 /* Make sure we don't actually unlink the file */
796 storeDiskdDirMapBitReset(SD
, e
->swap_filen
);
801 rb
->counts
.dupcount
++;
803 /* URL doesnt exist, swapfile not in use */
807 /* update store_swap_size */
808 rb
->counts
.objcount
++;
809 e
= storeDiskdDirAddDiskRestore(SD
, s
.key
,
818 (int) rb
->flags
.clean
);
819 storeDirSwapLog(e
, SWAP_LOG_ADD
);
821 eventAdd("storeRebuild", storeDiskdDirRebuildFromSwapLog
, rb
, 0.0, 1);
825 storeDiskdDirGetNextFile(RebuildState
* rb
, int *sfileno
, int *size
)
827 SwapDir
*SD
= rb
->sd
;
828 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)SD
->fsdata
;
832 debug(20, 3) ("storeDiskdDirGetNextFile: flag=%d, %d: /%02X/%02X\n",
839 while (fd
< 0 && rb
->done
== 0) {
841 if (0 == rb
->flags
.init
) { /* initialize, open first file */
847 assert(Config
.cacheSwap
.n_configured
> 0);
849 if (0 == rb
->in_dir
) { /* we need to read in a new directory */
850 snprintf(rb
->fullpath
, SQUID_MAXPATHLEN
, "%s/%02X/%02X",
852 rb
->curlvl1
, rb
->curlvl2
);
853 if (rb
->flags
.init
&& rb
->td
!= NULL
)
858 rb
->td
= opendir(rb
->fullpath
);
860 if (rb
->td
== NULL
) {
861 debug(50, 1) ("storeDiskdDirGetNextFile: opendir: %s: %s\n",
862 rb
->fullpath
, xstrerror());
864 rb
->entry
= readdir(rb
->td
); /* skip . and .. */
865 rb
->entry
= readdir(rb
->td
);
866 if (rb
->entry
== NULL
&& errno
== ENOENT
)
867 debug(20, 1) ("storeDiskdDirGetNextFile: directory does not exist!.\n");
868 debug(20, 3) ("storeDiskdDirGetNextFile: Directory %s\n", rb
->fullpath
);
871 if (rb
->td
!= NULL
&& (rb
->entry
= readdir(rb
->td
)) != NULL
) {
873 if (sscanf(rb
->entry
->d_name
, "%x", &rb
->fn
) != 1) {
874 debug(20, 3) ("storeDiskdDirGetNextFile: invalid %s\n",
878 if (!storeDiskdFilenoBelongsHere(rb
->fn
, rb
->sd
->index
, rb
->curlvl1
, rb
->curlvl2
)) {
879 debug(20, 3) ("storeDiskdDirGetNextFile: %08X does not belong in %d/%d/%d\n",
880 rb
->fn
, rb
->sd
->index
, rb
->curlvl1
, rb
->curlvl2
);
883 used
= storeDiskdDirMapBitTest(SD
, rb
->fn
);
885 debug(20, 3) ("storeDiskdDirGetNextFile: Locked, continuing with next.\n");
888 snprintf(rb
->fullfilename
, SQUID_MAXPATHLEN
, "%s/%s",
889 rb
->fullpath
, rb
->entry
->d_name
);
890 debug(20, 3) ("storeDiskdDirGetNextFile: Opening %s\n", rb
->fullfilename
);
891 fd
= file_open(rb
->fullfilename
, O_RDONLY
);
893 debug(50, 1) ("storeDiskdDirGetNextFile: %s: %s\n", rb
->fullfilename
, xstrerror());
895 store_open_disk_fd
++;
899 if (++rb
->curlvl2
< diskdinfo
->l2
)
902 if (++rb
->curlvl1
< diskdinfo
->l1
)
911 /* Add a new object to the cache with empty memory copy and pointer to disk
912 * use to rebuild store from disk. */
914 storeDiskdDirAddDiskRestore(SwapDir
*SD
, const cache_key
* key
,
925 StoreEntry
*e
= NULL
;
926 debug(20, 5) ("storeDiskdAddDiskRestore: %s, fileno=%08X\n", storeKeyText(key
), file_number
);
927 /* if you call this you'd better be sure file_number is not
929 e
= new_StoreEntry(STORE_ENTRY_WITHOUT_MEMOBJ
, NULL
, NULL
);
930 e
->store_status
= STORE_OK
;
931 storeSetMemStatus(e
, NOT_IN_MEMORY
);
932 e
->swap_status
= SWAPOUT_DONE
;
933 e
->swap_filen
= file_number
;
934 e
->swap_dirn
= SD
->index
;
935 e
->swap_file_sz
= swap_file_sz
;
937 #if !HEAP_REPLACEMENT
940 e
->lastref
= lastref
;
941 e
->timestamp
= timestamp
;
942 e
->expires
= expires
;
943 e
->lastmod
= lastmod
;
944 e
->refcount
= refcount
;
946 EBIT_SET(e
->flags
, ENTRY_CACHABLE
);
947 EBIT_CLR(e
->flags
, RELEASE_REQUEST
);
948 EBIT_CLR(e
->flags
, KEY_PRIVATE
);
949 e
->ping_status
= PING_NONE
;
950 EBIT_CLR(e
->flags
, ENTRY_VALIDATED
);
951 storeDiskdDirMapBitSet(SD
, e
->swap_filen
);
952 storeHashInsert(e
, key
); /* do it after we clear KEY_PRIVATE */
953 storeDiskdDirReplAdd(SD
, e
);
958 storeDiskdDirRebuild(SwapDir
* sd
)
960 RebuildState
*rb
= xcalloc(1, sizeof(*rb
));
966 rb
->speed
= opt_foreground_rebuild
? 1 << 30 : 50;
968 * If the swap.state file exists in the cache_dir, then
969 * we'll use storeDiskdDirRebuildFromSwapLog(), otherwise we'll
970 * use storeDiskdDirRebuildFromDirectory() to open up each file
971 * and suck in the meta data.
973 fp
= storeDiskdDirOpenTmpSwapLog(sd
, &clean
, &zero
);
974 if (fp
== NULL
|| zero
) {
977 func
= storeDiskdDirRebuildFromDirectory
;
979 func
= storeDiskdDirRebuildFromSwapLog
;
981 rb
->flags
.clean
= (unsigned int) clean
;
984 rb
->flags
.need_to_validate
= 1;
985 debug(20, 1) ("Rebuilding storage in %s (%s)\n",
986 sd
->path
, clean
? "CLEAN" : "DIRTY");
987 store_dirs_rebuilding
++;
988 cbdataAdd(rb
, cbdataXfree
, 0);
989 eventAdd("storeRebuild", func
, rb
, 0.0, 1);
993 storeDiskdDirCloseTmpSwapLog(SwapDir
* sd
)
995 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)sd
->fsdata
;
996 char *swaplog_path
= xstrdup(storeDiskdDirSwapLogFile(sd
, NULL
));
997 char *new_path
= xstrdup(storeDiskdDirSwapLogFile(sd
, ".new"));
999 file_close(diskdinfo
->swaplog_fd
);
1001 if (unlink(swaplog_path
) < 0) {
1002 debug(50, 0) ("%s: %s\n", swaplog_path
, xstrerror());
1003 fatal("storeDiskdDirCloseTmpSwapLog: unlink failed");
1006 if (xrename(new_path
, swaplog_path
) < 0) {
1007 fatal("storeDiskdDirCloseTmpSwapLog: rename failed");
1009 fd
= file_open(swaplog_path
, O_WRONLY
| O_CREAT
);
1011 debug(50, 1) ("%s: %s\n", swaplog_path
, xstrerror());
1012 fatal("storeDiskdDirCloseTmpSwapLog: Failed to open swap log.");
1014 safe_free(swaplog_path
);
1015 safe_free(new_path
);
1016 diskdinfo
->swaplog_fd
= fd
;
1017 debug(47, 3) ("Cache Dir #%d log opened on FD %d\n", sd
->index
, fd
);
1021 storeDiskdDirOpenTmpSwapLog(SwapDir
* sd
, int *clean_flag
, int *zero_flag
)
1023 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)sd
->fsdata
;
1024 char *swaplog_path
= xstrdup(storeDiskdDirSwapLogFile(sd
, NULL
));
1025 char *clean_path
= xstrdup(storeDiskdDirSwapLogFile(sd
, ".last-clean"));
1026 char *new_path
= xstrdup(storeDiskdDirSwapLogFile(sd
, ".new"));
1028 struct stat clean_sb
;
1031 if (stat(swaplog_path
, &log_sb
) < 0) {
1032 debug(47, 1) ("Cache Dir #%d: No log file\n", sd
->index
);
1033 safe_free(swaplog_path
);
1034 safe_free(clean_path
);
1035 safe_free(new_path
);
1038 *zero_flag
= log_sb
.st_size
== 0 ? 1 : 0;
1039 /* close the existing write-only FD */
1040 if (diskdinfo
->swaplog_fd
>= 0)
1041 file_close(diskdinfo
->swaplog_fd
);
1042 /* open a write-only FD for the new log */
1043 fd
= file_open(new_path
, O_WRONLY
| O_CREAT
| O_TRUNC
);
1045 debug(50, 1) ("%s: %s\n", new_path
, xstrerror());
1046 fatal("storeDirOpenTmpSwapLog: Failed to open swap log.");
1048 diskdinfo
->swaplog_fd
= fd
;
1049 /* open a read-only stream of the old log */
1050 fp
= fopen(swaplog_path
, "r");
1052 debug(50, 0) ("%s: %s\n", swaplog_path
, xstrerror());
1053 fatal("Failed to open swap log for reading");
1055 memset(&clean_sb
, '\0', sizeof(struct stat
));
1056 if (stat(clean_path
, &clean_sb
) < 0)
1058 else if (clean_sb
.st_mtime
< log_sb
.st_mtime
)
1062 safeunlink(clean_path
, 1);
1063 safe_free(swaplog_path
);
1064 safe_free(clean_path
);
1065 safe_free(new_path
);
1069 struct _clean_state
{
1074 off_t outbuf_offset
;
1078 #define CLEAN_BUF_SZ 16384
1080 * Begin the process to write clean cache state. For DISKD this means
1081 * opening some log files and allocating write buffers. Return 0 if
1082 * we succeed, and assign the 'func' and 'data' return pointers.
1085 storeDiskdDirWriteCleanOpen(SwapDir
* sd
)
1087 struct _clean_state
*state
= xcalloc(1, sizeof(*state
));
1089 sd
->log
.clean
.write
= NULL
;
1090 sd
->log
.clean
.state
= NULL
;
1091 state
->cur
= xstrdup(storeDiskdDirSwapLogFile(sd
, NULL
));
1092 state
->new = xstrdup(storeDiskdDirSwapLogFile(sd
, ".clean"));
1093 state
->cln
= xstrdup(storeDiskdDirSwapLogFile(sd
, ".last-clean"));
1094 state
->outbuf
= xcalloc(CLEAN_BUF_SZ
, 1);
1095 state
->outbuf_offset
= 0;
1098 state
->fd
= file_open(state
->new, O_WRONLY
| O_CREAT
| O_TRUNC
);
1101 debug(20, 3) ("storeDirWriteCleanLogs: opened %s, FD %d\n",
1102 state
->new, state
->fd
);
1104 if (stat(state
->cur
, &sb
) == 0)
1105 fchmod(state
->fd
, sb
.st_mode
);
1107 sd
->log
.clean
.write
= storeDiskdDirWriteCleanEntry
;
1108 sd
->log
.clean
.state
= state
;
1113 * "write" an entry to the clean log file.
1116 storeDiskdDirWriteCleanEntry(const StoreEntry
* e
, SwapDir
* sd
)
1119 static size_t ss
= sizeof(storeSwapLogData
);
1120 struct _clean_state
*state
= sd
->log
.clean
.state
;
1122 storeDiskdDirWriteCleanClose(sd
);
1125 memset(&s
, '\0', ss
);
1126 s
.op
= (char) SWAP_LOG_ADD
;
1127 s
.swap_filen
= e
->swap_filen
;
1128 s
.timestamp
= e
->timestamp
;
1129 s
.lastref
= e
->lastref
;
1130 s
.expires
= e
->expires
;
1131 s
.lastmod
= e
->lastmod
;
1132 s
.swap_file_sz
= e
->swap_file_sz
;
1133 s
.refcount
= e
->refcount
;
1135 xmemcpy(&s
.key
, e
->key
, MD5_DIGEST_CHARS
);
1136 xmemcpy(state
->outbuf
+ state
->outbuf_offset
, &s
, ss
);
1137 state
->outbuf_offset
+= ss
;
1138 /* buffered write */
1139 if (state
->outbuf_offset
+ ss
> CLEAN_BUF_SZ
) {
1140 if (write(state
->fd
, state
->outbuf
, state
->outbuf_offset
) < 0) {
1141 debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
1142 state
->new, xstrerror());
1143 debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile not replaced.\n");
1144 file_close(state
->fd
);
1148 sd
->log
.clean
.state
= NULL
;
1149 sd
->log
.clean
.write
= NULL
;
1151 state
->outbuf_offset
= 0;
1156 storeDiskdDirWriteCleanClose(SwapDir
* sd
)
1158 struct _clean_state
*state
= sd
->log
.clean
.state
;
1161 if (write(state
->fd
, state
->outbuf
, state
->outbuf_offset
) < 0) {
1162 debug(50, 0) ("storeDirWriteCleanLogs: %s: write: %s\n",
1163 state
->new, xstrerror());
1164 debug(20, 0) ("storeDirWriteCleanLogs: Current swap logfile "
1166 file_close(state
->fd
);
1170 safe_free(state
->outbuf
);
1172 * You can't rename open files on Microsoft "operating systems"
1173 * so we have to close before renaming.
1175 storeDiskdDirCloseSwapLog(sd
);
1177 if (state
->fd
>= 0) {
1179 file_close(state
->fd
);
1181 if (unlink(cur
) < 0)
1182 debug(50, 0) ("storeDirWriteCleanLogs: unlinkd failed: %s, %s\n",
1185 xrename(state
->new, state
->cur
);
1187 /* touch a timestamp file if we're not still validating */
1188 if (store_dirs_rebuilding
)
1190 else if (state
->fd
< 0)
1193 file_close(file_open(state
->cln
, O_WRONLY
| O_CREAT
| O_TRUNC
));
1195 safe_free(state
->cur
);
1196 safe_free(state
->new);
1197 safe_free(state
->cln
);
1199 file_close(state
->fd
);
1202 sd
->log
.clean
.state
= NULL
;
1203 sd
->log
.clean
.write
= NULL
;
1207 storeDiskdDirSwapLog(const SwapDir
* sd
, const StoreEntry
* e
, int op
)
1209 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)sd
->fsdata
;
1210 storeSwapLogData
*s
= xcalloc(1, sizeof(storeSwapLogData
));
1212 s
->swap_filen
= e
->swap_filen
;
1213 s
->timestamp
= e
->timestamp
;
1214 s
->lastref
= e
->lastref
;
1215 s
->expires
= e
->expires
;
1216 s
->lastmod
= e
->lastmod
;
1217 s
->swap_file_sz
= e
->swap_file_sz
;
1218 s
->refcount
= e
->refcount
;
1219 s
->flags
= e
->flags
;
1220 xmemcpy(s
->key
, e
->key
, MD5_DIGEST_CHARS
);
1221 file_write(diskdinfo
->swaplog_fd
,
1224 sizeof(storeSwapLogData
),
1231 storeDiskdDirNewfs(SwapDir
* sd
)
1233 debug(47, 3) ("Creating swap space in %s\n", sd
->path
);
1234 storeDiskdDirCreateDirectory(sd
->path
, 0);
1235 storeDiskdDirCreateSwapSubDirs(sd
);
1239 rev_int_sort(const void *A
, const void *B
)
1247 storeDiskdDirClean(int swap_index
)
1250 struct dirent
*de
= NULL
;
1251 LOCAL_ARRAY(char, p1
, MAXPATHLEN
+ 1);
1252 LOCAL_ARRAY(char, p2
, MAXPATHLEN
+ 1);
1258 int fn
; /* same as swapfileno, but with dirn bits set */
1264 diskdinfo_t
*diskdinfo
;
1266 D0
= diskd_dir_index
[swap_index
% N0
];
1267 SD
= &Config
.cacheSwap
.swapDirs
[D0
];
1268 diskdinfo
= (diskdinfo_t
*)SD
->fsdata
;
1270 D1
= (swap_index
/ N0
) % N1
;
1272 D2
= ((swap_index
/ N0
) / N1
) % N2
;
1273 snprintf(p1
, SQUID_MAXPATHLEN
, "%s/%02X/%02X",
1274 Config
.cacheSwap
.swapDirs
[D0
].path
, D1
, D2
);
1275 debug(36, 3) ("storeDirClean: Cleaning directory %s\n", p1
);
1278 if (errno
== ENOENT
) {
1279 debug(36, 0) ("storeDirClean: WARNING: Creating %s\n", p1
);
1280 if (mkdir(p1
, 0777) == 0)
1283 debug(50, 0) ("storeDirClean: %s: %s\n", p1
, xstrerror());
1287 while ((de
= readdir(dp
)) != NULL
&& k
< 20) {
1288 if (sscanf(de
->d_name
, "%X", &swapfileno
) != 1)
1290 fn
= swapfileno
; /* XXX should remove this cruft ! */
1291 if (storeDiskdDirValidFileno(SD
, fn
))
1292 if (storeDiskdDirMapBitTest(SD
, fn
))
1293 if (storeDiskdFilenoBelongsHere(fn
, D0
, D1
, D2
))
1296 if (!stat(de
->d_name
, &sb
))
1297 if (sb
.st_size
== 0)
1300 files
[k
++] = swapfileno
;
1305 qsort(files
, k
, sizeof(int), rev_int_sort
);
1308 for (n
= 0; n
< k
; n
++) {
1309 debug(36, 3) ("storeDirClean: Cleaning file %08X\n", files
[n
]);
1310 snprintf(p2
, MAXPATHLEN
+ 1, "%s/%08X", p1
, files
[n
]);
1316 Counter
.swap_files_cleaned
++;
1318 debug(36, 3) ("Cleaned %d unused files from %s\n", k
, p1
);
1323 storeDiskdDirCleanEvent(void *unused
)
1325 static int swap_index
= 0;
1330 * Assert that there are DISKD cache_dirs configured, otherwise
1331 * we should never be called.
1333 assert(n_diskd_dirs
);
1334 if (NULL
== diskd_dir_index
) {
1336 diskdinfo_t
*diskdinfo
;
1338 * Initialize the little array that translates DISKD cache_dir
1339 * number into the Config.cacheSwap.swapDirs array index.
1341 diskd_dir_index
= xcalloc(n_diskd_dirs
, sizeof(*diskd_dir_index
));
1342 for (i
= 0, n
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
1343 sd
= &Config
.cacheSwap
.swapDirs
[i
];
1344 if (!storeDiskdDirIs(sd
))
1346 diskd_dir_index
[n
++] = i
;
1347 diskdinfo
= (diskdinfo_t
*)sd
->fsdata
;
1348 j
+= (diskdinfo
->l1
* diskdinfo
->l2
);
1350 assert(n
== n_diskd_dirs
);
1352 * Start the storeDiskdDirClean() swap_index with a random
1353 * value. j equals the total number of DISKD level 2
1356 swap_index
= (int) (squid_random() % j
);
1358 if (0 == store_dirs_rebuilding
) {
1359 n
= storeDiskdDirClean(swap_index
);
1362 eventAdd("storeDirClean", storeDiskdDirCleanEvent
, NULL
,
1363 15.0 * exp(-0.25 * n
), 1);
1367 storeDiskdDirIs(SwapDir
* sd
)
1369 if (strncmp(sd
->type
, "diskd", 3) == 0)
1375 * Does swapfile number 'fn' belong in cachedir #F0,
1376 * level1 dir #F1, level2 dir #F2?
1379 storeDiskdFilenoBelongsHere(int fn
, int F0
, int F1
, int F2
)
1384 diskdinfo_t
*diskdinfo
;
1385 assert(F0
< Config
.cacheSwap
.n_configured
);
1386 diskdinfo
= (diskdinfo_t
*)Config
.cacheSwap
.swapDirs
[F0
].fsdata
;
1389 D1
= ((filn
/ L2
) / L2
) % L1
;
1392 D2
= (filn
/ L2
) % L2
;
1399 storeDiskdDirValidFileno(SwapDir
*SD
, sfileno filn
)
1401 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)SD
->fsdata
;
1404 if (filn
> diskdinfo
->map
->max_n_files
)
1410 storeDiskdDirMaintain(SwapDir
*SD
)
1412 StoreEntry
*e
= NULL
;
1419 static time_t last_warn_time
= 0;
1420 #if !HEAP_REPLACEMENT
1422 dlink_node
*prev
= NULL
;
1425 heap_key min_age
= 0.0;
1426 link_list
*locked_entries
= NULL
;
1427 #if HEAP_REPLACEMENT_DEBUG
1428 if (!verify_heap_property(SD
->repl
.heap
.heap
)) {
1429 debug(20, 1) ("Heap property violated!\n");
1433 /* We can't delete objects while rebuilding swap */
1434 if (store_dirs_rebuilding
) {
1437 f
= (double) (store_swap_size
- store_swap_low
) / (store_swap_high
- store_swap_low
);
1438 f
= f
< 0.0 ? 0.0 : f
> 1.0 ? 1.0 : f
;
1439 max_scan
= (int) (f
* 400.0 + 100.0);
1440 max_remove
= (int) (f
* 70.0 + 10.0);
1442 * This is kinda cheap, but so we need this priority hack?
1445 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace
, NULL
, 1.0 - f
, 1);
1448 debug(20, 3) ("storeMaintainSwapSpace: f=%f, max_scan=%d, max_remove=%d\n", f
, max_scan
, max_remove
);
1449 #if HEAP_REPLACEMENT
1450 while (heap_nodes(SD
->repl
.heap
.heap
) > 0) {
1451 if (store_swap_size
< store_swap_low
)
1453 if (expired
>= max_remove
)
1455 if (scanned
>= max_scan
)
1457 age
= heap_peepminkey(SD
->repl
.heap
.heap
);
1458 e
= heap_extractmin(SD
->repl
.heap
.heap
);
1459 e
->repl
.node
= NULL
; /* no longer in the heap */
1461 if (storeEntryLocked(e
)) {
1463 * Entry is in use ... put it in a linked list to ignore it.
1465 if (!EBIT_TEST(e
->flags
, ENTRY_SPECIAL
)) {
1467 * If this was a "SPECIAL" do not add it back into the heap.
1468 * It will always be "SPECIAL" and therefore never removed.
1470 debug(20, 4) ("storeDiskdDirMaintain: locked url %s\n",
1471 (e
->mem_obj
&& e
->mem_obj
->url
) ? e
->mem_obj
->url
: storeKeyText(e
->
1473 linklistPush(&locked_entries
, e
);
1477 } else if (storeDiskdDirCheckExpired(SD
, e
)) {
1479 * Note: This will not check the reference age ifdef
1480 * HEAP_REPLACEMENT, but it does some other useful
1484 debug(20, 3) ("Released store object age %f size %d refs %d key %s\n",
1485 age
, e
->swap_file_sz
, e
->refcount
, storeKeyText(e
->key
));
1490 * Did not expire the object so we need to add it back
1493 debug(20, 5) ("storeMaintainSwapSpace: non-expired %s\n",
1494 storeKeyText(e
->key
));
1495 linklistPush(&locked_entries
, e
);
1498 if (store_swap_size
< store_swap_low
)
1500 else if (expired
>= max_remove
)
1502 else if (scanned
>= max_scan
)
1506 * Bump the heap age factor.
1509 SD
->repl
.heap
.heap
->age
= min_age
;
1511 * Reinsert all bumped locked entries back into heap...
1513 while ((e
= linklistShift(&locked_entries
)))
1514 e
->repl
.node
= heap_insert(SD
->repl
.heap
.heap
, e
);
1516 for (m
= SD
->repl
.lru
.list
.tail
; m
; m
= prev
) {
1520 if (storeEntryLocked(e
)) {
1522 * If there is a locked entry at the tail of the LRU list,
1523 * move it to the beginning to get it out of the way.
1524 * Theoretically, we might have all locked objects at the
1525 * tail, and then we'll never remove anything here and the
1526 * LRU age will go to zero.
1528 if (memInUse(MEM_STOREENTRY
) > max_scan
) {
1529 dlinkDelete(&e
->repl
.lru
, &SD
->repl
.lru
.list
);
1530 dlinkAdd(e
, &e
->repl
.lru
, &SD
->repl
.lru
.list
);
1534 } else if (storeDiskdDirCheckExpired(SD
, e
)) {
1538 if (expired
>= max_remove
)
1540 if (scanned
>= max_scan
)
1544 debug(20, (expired
? 2 : 3)) ("storeMaintainSwapSpace: scanned %d/%d removed %d/%d l
1545 ocked %d f=%.03f\n",
1546 scanned
, max_scan
, expired
, max_remove
, locked
, f
);
1547 debug(20, 3) ("storeMaintainSwapSpace stats:\n");
1548 debug(20, 3) (" %6d objects\n", memInUse(MEM_STOREENTRY
));
1549 debug(20, 3) (" %6d were scanned\n", scanned
);
1550 debug(20, 3) (" %6d were locked\n", locked
);
1551 debug(20, 3) (" %6d were expired\n", expired
);
1552 if (store_swap_size
< Config
.Swap
.maxSize
)
1554 if (squid_curtime
- last_warn_time
< 10)
1556 debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n",
1557 store_swap_size
, Config
.Swap
.maxSize
);
1558 last_warn_time
= squid_curtime
;
1562 * storeDiskdDirCheckObj
1564 * This routine is called by storeDirSelectSwapDir to see if the given
1565 * object is able to be stored on this filesystem. DISKD filesystems will
1566 * happily store anything as long as the LRU time isn't too small.
1569 storeDiskdDirCheckObj(SwapDir
*SD
, const StoreEntry
*e
)
1573 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)SD
->fsdata
;
1574 #if !HEAP_REPLACEMENT
1575 if (storeDiskdDirExpiredReferenceAge(SD
) < 300) {
1576 debug(20, 3) ("storeDiskdDirCheckObj: NO: LRU Age = %d\n",
1577 storeDiskdDirExpiredReferenceAge(SD
));
1578 /* store_check_cachable_hist.no.lru_age_too_low++; */
1583 /* Check the queue length */
1584 if (diskdinfo
->away
>= diskdinfo
->magic1
)
1587 /* Calculate the storedir load relative to magic2 on a scale of 0 .. 1000 */
1588 if (diskdinfo
->away
== 0)
1591 loadav
= diskdinfo
->magic2
* 1000 / diskdinfo
->away
;
1596 * storeDiskdDirRefObj
1598 * This routine is called whenever an object is referenced, so we can
1599 * maintain replacement information within the storage fs.
1602 storeDiskdDirRefObj(SwapDir
*SD
, StoreEntry
*e
)
1604 debug(1, 3) ("storeDiskdDirRefObj: referencing %p %d/%d\n", e
, e
->swap_dirn
,
1606 #if HEAP_REPLACEMENT
1607 /* Nothing to do here */
1609 /* Reference the object */
1610 if (!EBIT_TEST(e
->flags
, RELEASE_REQUEST
) &&
1611 !EBIT_TEST(e
->flags
, ENTRY_SPECIAL
)) {
1612 dlinkDelete(&e
->repl
.lru
, &SD
->repl
.lru
.list
);
1613 dlinkAdd(e
, &e
->repl
.lru
, &SD
->repl
.lru
.list
);
1619 * storeDiskdDirUnrefObj
1620 * This routine is called whenever the last reference to an object is
1621 * removed, to maintain replacement information within the storage fs.
1624 storeDiskdDirUnrefObj(SwapDir
*SD
, StoreEntry
*e
)
1626 debug(1, 3) ("storeDiskdDirUnrefObj: referencing %p %d/%d\n", e
,
1627 e
->swap_dirn
, e
->swap_filen
);
1628 #if HEAP_REPLACEMENT
1630 heap_update(SD
->repl
.heap
.heap
, e
->repl
.node
, e
);
1635 * storeDiskdDirUnlinkFile
1637 * This is a *synchronous* unlink which is currently used in the rebuild
1638 * process. This is bad, but it'll have to stay until the dir rebuild
1639 * uses storeDiskdUnlink() ..
1642 storeDiskdDirUnlinkFile(SwapDir
*SD
, sfileno f
)
1644 debug(79, 3) ("storeDiskdDirUnlinkFile: unlinking fileno %08X\n", f
);
1645 storeDiskdDirMapBitReset(SD
, f
);
1646 unlinkdUnlink(storeDiskdDirFullPath(SD
, f
, NULL
));
1649 #if !HEAP_REPLACEMENT
1651 * storeDiskdDirExpiredReferenceAge
1653 * The LRU age is scaled exponentially between 1 minute and
1654 * Config.referenceAge , when store_swap_low < store_swap_size <
1655 * store_swap_high. This keeps store_swap_size within the low and high
1656 * water marks. If the cache is very busy then store_swap_size stays
1657 * closer to the low water mark, if it is not busy, then it will stay
1658 * near the high water mark. The LRU age value can be examined on the
1659 * cachemgr 'info' page.
1662 storeDiskdDirExpiredReferenceAge(SwapDir
*SD
)
1667 long store_high
, store_low
;
1669 store_high
= (long) (((float) SD
->max_size
*
1670 (float) Config
.Swap
.highWaterMark
) / (float) 100);
1671 store_low
= (long) (((float) SD
->max_size
*
1672 (float) Config
.Swap
.lowWaterMark
) / (float) 100);
1673 debug(20, 20) ("RA: Dir %s, hi=%d, lo=%d, cur=%d\n", SD
->path
, store_high
, store_low
, SD
->cur_size
);
1675 x
= (double) (store_high
- SD
->cur_size
) /
1676 (store_high
- store_low
);
1677 x
= x
< 0.0 ? 0.0 : x
> 1.0 ? 1.0 : x
;
1678 z
= pow((double) (Config
.referenceAge
/ 60), x
);
1679 age
= (time_t) (z
* 60.0);
1682 else if (age
> Config
.referenceAge
)
1683 age
= Config
.referenceAge
;
1689 * storeDiskdDirCheckExpired
1691 * Check whether the given object is expired or not
1692 * It breaks layering a little by calling the upper layers to find
1693 * out whether the object is locked or not, but we can't help this
1697 storeDiskdDirCheckExpired(SwapDir
*SD
, StoreEntry
*e
)
1699 if (storeEntryLocked(e
))
1701 if (EBIT_TEST(e
->flags
, RELEASE_REQUEST
))
1703 if (EBIT_TEST(e
->flags
, ENTRY_NEGCACHED
) && squid_curtime
>= e
->expires
)
1706 #if HEAP_REPLACEMENT
1708 * with HEAP_REPLACEMENT we are not using the LRU reference age, the heap
1709 * controls the replacement of objects.
1713 if (squid_curtime
- e
->lastref
> storeDiskdDirExpiredReferenceAge(SD
))
1720 * Add and remove the given StoreEntry from the replacement policy in
1725 storeDiskdDirReplAdd(SwapDir
*SD
, StoreEntry
*e
)
1727 debug(20, 4) ("storeDiskdDirReplAdd: added node %p to dir %d\n", e
,
1729 #if HEAP_REPLACEMENT
1730 if (EBIT_TEST(e
->flags
, ENTRY_SPECIAL
)) {
1733 e
->repl
.node
= heap_insert(SD
->repl
.heap
.heap
, e
);
1734 debug(20, 4) ("storeDiskdDirReplAdd: inserted node 0x%x\n", e
->repl
.node
);
1737 /* Shouldn't we not throw special objects into the lru ? */
1738 dlinkAdd(e
, &e
->repl
.lru
, &SD
->repl
.lru
.list
);
1744 storeDiskdDirReplRemove(StoreEntry
*e
)
1746 SwapDir
*SD
= INDEXSD(e
->swap_dirn
);
1747 debug(20, 4) ("storeDiskdDirReplRemove: remove node %p from dir %d\n", e
,
1749 #if HEAP_REPLACEMENT
1750 /* And now, release the object from the replacement policy */
1752 debug(20, 4) ("storeDiskdDirReplRemove: deleting node 0x%x\n",
1754 heap_delete(SD
->repl
.heap
.heap
, e
->repl
.node
);
1755 e
->repl
.node
= NULL
;
1758 dlinkDelete(&e
->repl
.lru
, &SD
->repl
.lru
.list
);
1765 * SHM manipulation routines
1769 storeDiskdShmGet(SwapDir
* sd
, int *shm_offset
)
1772 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)sd
->fsdata
;
1773 buf
= linklistShift(&diskdinfo
->shm
.stack
);
1775 *shm_offset
= buf
- diskdinfo
->shm
.buf
;
1776 assert(0 <= *shm_offset
&& *shm_offset
< SHMBUFS
* SHMBUF_BLKSZ
);
1777 diskd_stats
.shmbuf_count
++;
1782 storeDiskdShmPut(SwapDir
* sd
, int offset
)
1785 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)sd
->fsdata
;
1786 assert(offset
>= 0);
1787 assert(offset
< SHMBUFS
* SHMBUF_BLKSZ
);
1788 buf
= diskdinfo
->shm
.buf
+ offset
;
1789 linklistPush(&diskdinfo
->shm
.stack
, buf
);
1790 diskd_stats
.shmbuf_count
--;
1796 /* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
1799 storeDiskdDirStats(SwapDir
*SD
, StoreEntry
* sentry
)
1801 diskdinfo_t
*diskdinfo
;
1805 diskdinfo
= (diskdinfo_t
*)SD
->fsdata
;
1806 storeAppendPrintf(sentry
, "First level subdirectories: %d\n", diskdinfo
->l1
);
1807 storeAppendPrintf(sentry
, "Second level subdirectories: %d\n", diskdinfo
->l2
);
1808 storeAppendPrintf(sentry
, "Maximum Size: %d KB\n", SD
->max_size
);
1809 storeAppendPrintf(sentry
, "Current Size: %d KB\n", SD
->cur_size
);
1810 storeAppendPrintf(sentry
, "Percent Used: %0.2f%%\n",
1811 100.0 * SD
->cur_size
/ SD
->max_size
);
1812 storeAppendPrintf(sentry
, "Filemap bits in use: %d of %d (%d%%)\n",
1813 diskdinfo
->map
->n_files_in_map
, diskdinfo
->map
->max_n_files
,
1814 percent(diskdinfo
->map
->n_files_in_map
, diskdinfo
->map
->max_n_files
));
1816 #define fsbtoblk(num, fsbs, bs) \
1817 (((fsbs) != 0 && (fsbs) < (bs)) ? \
1818 (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
1819 if (!statvfs(SD
->path
, &sfs
)) {
1820 storeAppendPrintf(sentry
, "Filesystem Space in use: %d/%d KB (%d%%)\n",
1821 fsbtoblk((sfs
.f_blocks
- sfs
.f_bfree
), sfs
.f_frsize
, 1024),
1822 fsbtoblk(sfs
.f_blocks
, sfs
.f_frsize
, 1024),
1823 percent(sfs
.f_blocks
- sfs
.f_bfree
, sfs
.f_blocks
));
1824 storeAppendPrintf(sentry
, "Filesystem Inodes in use: %d/%d (%d%%)\n",
1825 sfs
.f_files
- sfs
.f_ffree
, sfs
.f_files
,
1826 percent(sfs
.f_files
- sfs
.f_ffree
, sfs
.f_files
));
1829 storeAppendPrintf(sentry
, "Flags:");
1830 if (SD
->flags
.selected
)
1831 storeAppendPrintf(sentry
, " SELECTED");
1832 if (SD
->flags
.read_only
)
1833 storeAppendPrintf(sentry
, " READ-ONLY");
1834 storeAppendPrintf(sentry
, "\n");
1835 #if !HEAP_REPLACEMENT
1836 storeAppendPrintf(sentry
, "LRU Expiration Age: %6.2f days\n",
1837 (double) storeDiskdDirExpiredReferenceAge(SD
) / 86400.0);
1840 storeAppendPrintf(sentry
, "Storage Replacement Threshold:\t%f\n",
1841 heap_peepminkey(sd
.repl
.heap
.heap
));
1844 storeAppendPrintf(sentry
, "Pending operations: %d\n", diskdinfo
->away
);
1848 * storeDiskdDirReconfigure
1850 * This routine is called when the given swapdir needs reconfiguring
1853 storeDiskdDirReconfigure(SwapDir
*sd
, int index
, char *path
)
1861 unsigned int read_only
= 0;
1862 diskdinfo_t
*diskdinfo
;
1865 size
= i
<< 10; /* Mbytes to kbytes */
1867 fatal("storeDiskdDirReconfigure: invalid size value");
1871 fatal("storeDiskdDirReconfigure: invalid level 1 directories value");
1875 fatal("storeDiskdDirReconfigure: invalid level 2 directories value");
1879 fatal("storeDiskdDirParse: invalid magic1 value");
1883 fatal("storeDiskdDirParse: invalid magic2 value");
1884 if ((token
= strtok(NULL
, w_space
)))
1885 if (!strcasecmp(token
, "read-only"))
1888 /* just reconfigure it */
1889 if (size
== sd
->max_size
)
1890 debug(3, 1) ("Cache dir '%s' size remains unchanged at %d KB\n",
1893 debug(3, 1) ("Cache dir '%s' size changed to %d KB\n",
1895 sd
->max_size
= size
;
1896 if (sd
->flags
.read_only
!= read_only
)
1897 debug(3, 1) ("Cache dir '%s' now %s\n",
1898 path
, read_only
? "Read-Only" : "Read-Write");
1899 diskdinfo
= (diskdinfo_t
*)sd
->fsdata
;
1900 diskdinfo
->magic1
= magic1
;
1901 diskdinfo
->magic2
= magic2
;
1902 sd
->flags
.read_only
= read_only
;
1907 storeDiskdDirDump(StoreEntry
* entry
, const char *name
, SwapDir
* s
)
1909 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)s
->fsdata
;
1910 storeAppendPrintf(entry
, "%s %s %s %d %d %d\n",
1920 * Only "free" the filesystem specific stuff here
1923 storeDiskdDirFree(SwapDir
* s
)
1925 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)s
->fsdata
;
1926 if (diskdinfo
->swaplog_fd
> -1) {
1927 file_close(diskdinfo
->swaplog_fd
);
1928 diskdinfo
->swaplog_fd
= -1;
1930 filemapFreeMemory(diskdinfo
->map
);
1932 s
->fsdata
= NULL
; /* Will aid debugging... */
1937 storeDiskdDirFullPath(SwapDir
*SD
, sfileno filn
, char *fullpath
)
1939 LOCAL_ARRAY(char, fullfilename
, SQUID_MAXPATHLEN
);
1940 diskdinfo_t
*diskdinfo
= (diskdinfo_t
*)SD
->fsdata
;
1941 int L1
= diskdinfo
->l1
;
1942 int L2
= diskdinfo
->l2
;
1944 fullpath
= fullfilename
;
1946 snprintf(fullpath
, SQUID_MAXPATHLEN
, "%s/%02X/%02X/%08X",
1948 ((filn
/ L2
) / L2
) % L1
,
1955 * storeDiskdCleanupDoubleCheck
1957 * This is called by storeCleanup() if -S was given on the command line.
1960 storeDiskdCleanupDoubleCheck(SwapDir
*sd
, StoreEntry
*e
)
1964 if (stat(storeDiskdDirFullPath(sd
, e
->swap_filen
, NULL
), &sb
) < 0) {
1965 debug(20, 0) ("storeDiskdCleanupDoubleCheck: MISSING SWAP FILE\n");
1966 debug(20, 0) ("storeDiskdCleanupDoubleCheck: FILENO %08X\n", e
->swap_filen
);
1967 debug(20, 0) ("storeDiskdCleanupDoubleCheck: PATH %s\n",
1968 storeDiskdDirFullPath(sd
, e
->swap_filen
, NULL
));
1969 storeEntryDump(e
, 0);
1972 if (e
->swap_file_sz
!= sb
.st_size
) {
1973 debug(20, 0) ("storeDiskdCleanupDoubleCheck: SIZE MISMATCH\n");
1974 debug(20, 0) ("storeDiskdCleanupDoubleCheck: FILENO %08X\n", e
->swap_filen
);
1975 debug(20, 0) ("storeDiskdCleanupDoubleCheck: PATH %s\n",
1976 storeDiskdDirFullPath(sd
, e
->swap_filen
, NULL
));
1977 debug(20, 0) ("storeDiskdCleanupDoubleCheck: ENTRY SIZE: %d, FILE SIZE: %d\n",
1978 e
->swap_file_sz
, (int) sb
.st_size
);
1979 storeEntryDump(e
, 0);
1986 * storeDiskdDirParse
1988 * Called when a *new* fs is being setup.
1991 storeDiskdDirParse(SwapDir
*sd
, int index
, char *path
)
1999 unsigned int read_only
= 0;
2000 diskdinfo_t
*diskdinfo
;
2003 size
= i
<< 10; /* Mbytes to kbytes */
2005 fatal("storeDiskdDirParse: invalid size value");
2009 fatal("storeDiskdDirParse: invalid level 1 directories value");
2013 fatal("storeDiskdDirParse: invalid level 2 directories value");
2017 fatal("storeDiskdDirParse: invalid magic1 value");
2021 fatal("storeDiskdDirParse: invalid magic2 value");
2024 if ((token
= strtok(NULL
, w_space
)))
2025 if (!strcasecmp(token
, "read-only"))
2028 diskdinfo
= xmalloc(sizeof(diskdinfo_t
));
2029 if (diskdinfo
== NULL
)
2030 fatal("storeDiskdDirParse: couldn't xmalloc() diskdinfo_t!\n");
2033 sd
->path
= xstrdup(path
);
2034 sd
->max_size
= size
;
2035 sd
->fsdata
= diskdinfo
;
2038 diskdinfo
->swaplog_fd
= -1;
2039 diskdinfo
->map
= NULL
; /* Debugging purposes */
2040 diskdinfo
->suggest
= 0;
2041 diskdinfo
->magic1
= magic1
;
2042 diskdinfo
->magic2
= magic2
;
2043 sd
->flags
.read_only
= read_only
;
2044 sd
->init
= storeDiskdDirInit
;
2045 sd
->newfs
= storeDiskdDirNewfs
;
2046 sd
->dump
= storeDiskdDirDump
;
2047 sd
->freefs
= storeDiskdDirFree
;
2048 sd
->dblcheck
= storeDiskdCleanupDoubleCheck
;
2049 sd
->statfs
= storeDiskdDirStats
;
2050 sd
->maintainfs
= storeDiskdDirMaintain
;
2051 sd
->checkobj
= storeDiskdDirCheckObj
;
2052 sd
->refobj
= storeDiskdDirRefObj
;
2053 sd
->unrefobj
= storeDiskdDirUnrefObj
;
2054 sd
->callback
= storeDiskdDirCallback
;
2055 sd
->sync
= storeDiskdDirSync
;
2056 sd
->obj
.create
= storeDiskdCreate
;
2057 sd
->obj
.open
= storeDiskdOpen
;
2058 sd
->obj
.close
= storeDiskdClose
;
2059 sd
->obj
.read
= storeDiskdRead
;
2060 sd
->obj
.write
= storeDiskdWrite
;
2061 sd
->obj
.unlink
= storeDiskdUnlink
;
2062 sd
->log
.open
= storeDiskdDirOpenSwapLog
;
2063 sd
->log
.close
= storeDiskdDirCloseSwapLog
;
2064 sd
->log
.write
= storeDiskdDirSwapLog
;
2065 sd
->log
.clean
.open
= storeDiskdDirWriteCleanOpen
;
2067 /* Initialise replacement policy stuff */
2068 #if HEAP_REPLACEMENT
2070 * Create new heaps with cache replacement policies attached to them.
2071 * The cache replacement policy is specified as either GDSF or LFUDA in
2072 * the squid.conf configuration file. Note that the replacement policy
2073 * applies only to the disk replacement algorithm. Memory replacement
2074 * always uses GDSF since we want to maximize object hit rate.
2076 if (Config
.replPolicy
) {
2077 if (tolower(Config
.replPolicy
[0]) == 'g') {
2078 debug(20, 1) ("Using GDSF disk replacement policy\n");
2079 sd
->repl
.heap
.heap
= new_heap(10000, HeapKeyGen_StoreEntry_GDSF
);
2080 } else if (tolower(Config
.replPolicy
[0]) == 'l') {
2081 if (tolower(Config
.replPolicy
[1]) == 'f') {
2082 debug(20, 1) ("Using LFUDA disk replacement policy\n");
2083 sd
->repl
.heap
.heap
= new_heap(10000, HeapKeyGen_StoreEntry_LFUDA
);
2084 } else if (tolower(Config
.replPolicy
[1]) == 'r') {
2085 debug(20, 1) ("Using LRU heap disk replacement policy\n");
2086 sd
->repl
.heap
.heap
= new_heap(10000, HeapKeyGen_StoreEntry_LRU
);
2089 debug(20, 1) ("Unrecognized replacement_policy; using GDSF\n");
2090 sd
->repl
.heap
.heap
= new_heap(10000, HeapKeyGen_StoreEntry_GDSF
);
2093 debug(20, 1) ("Using default disk replacement policy (GDSF)\n");
2094 sd
->repl
.heap
.heap
= new_heap(10000, HeapKeyGen_StoreEntry_GDSF
);
2097 sd
->repl
.lru
.list
.head
= NULL
;
2098 sd
->repl
.lru
.list
.tail
= NULL
;
2103 * Initial setup / end destruction
2106 storeDiskdDirDone(void)
2108 memPoolDestroy(diskd_state_pool
);
2109 diskd_initialised
= 0;
2113 storeFsSetup_diskd(storefs_entry_t
*storefs
)
2115 assert(!diskd_initialised
);
2116 storefs
->parsefunc
= storeDiskdDirParse
;
2117 storefs
->reconfigurefunc
= storeDiskdDirReconfigure
;
2118 storefs
->donefunc
= storeDiskdDirDone
;
2119 diskd_state_pool
= memPoolCreate("DISKD IO State data", sizeof(diskdstate_t
));
2120 memset(&diskd_stats
, '\0', sizeof(diskd_stats
));
2121 cachemgrRegister("diskd", "DISKD Stats", storeDiskdStats
, 0, 1);
2122 debug(81, 1) ("diskd started\n");
2123 diskd_initialised
= 1;